• CQRS:CQRS+AJAX架构 之 查询(Q)模型设计


    背景

    准备采用CQRS架构,之前也简单的应用过(只是把读和写在程序级别进行了分离),这篇文章是我最近几天的思考,写下来希望大家多提意见。这篇文章不会涉及Command端的设计,重点关注如何设计查询。

    真心的希望大家看完后能给出你们的意见和想法。

    什么是CQRS

    CQRS:Command Query Responsibility Separation。我喜欢职责分离,这也是我采用这种架构的原因,确实能带来单一职责的优点。

    简单的CQRS

    复杂的CQRS

    CQRS的常见查询需求

    下面是系统的一些查询需求:

    查询面板

    高级查询

    数据行级别的权限

    如:个人、部门、分公司、品种。

    固定约束

    如:启用、合法、租户ID。

    需求总结

    CQRS的查询设计

    充分利用SQL和动态类型的优势,不做太多无谓的封装。

    关键决策:

      1. 直接查询数据库返回Dynamic类型,不需要定义强类型。
      2. 直接用SQL,支持动态查询面板和动态数据行权限。目前没有找到封装SQL的理由,最多是在外围再封装一层,但是不会隐藏SQL(我之前写过一个简单的查询对象)。
      3. 利用一些策略防止SQL注入和权限提升(这篇文章不介绍)。

    示例代码

    下载地址:http://happy.codeplex.com/SourceControl/latest

    AJAX程序

     1 /// <reference path="Ext/ext-all-debug-w-comments.js" />
     2 
     3 Ext.onReady(function () {
     4     var query = {
     5         TableOrViewName: 'Users',
     6         WhereClause: "Name NOT LIKE '%段%'"
     7     };
     8 
     9     Ext.Ajax.request({
    10         url: 'TestDynamicQuery/Fetch',
    11         method: 'POST',
    12         params: { query: Ext.encode(query) },
    13         success: function (response) {
    14             console.log(response.responseText);
    15         }
    16     });
    17 
    18     query = {
    19         TableOrViewName: 'Users',
    20         WhereClause: "Age >= 20 AND Age <= 27"
    21     };
    22 
    23     Ext.Ajax.request({
    24         url: 'TestDynamicQuery/Fetch',
    25         method: 'POST',
    26         params: { query: Ext.encode(query) },
    27         success: function (response) {
    28             console.log(response.responseText);
    29         }
    30     });
    31 });

    万能查询控制器

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using System.Web.Mvc;
     7 
     8 using Newtonsoft.Json;
     9 
    10 using Happy.Query;
    11 
    12 namespace Happy.Web.Mvc
    13 {
    14     /// <summary>
    15     /// 动态查询控制器。
    16     /// </summary>
    17     public abstract class DynamicQueryController<TDynamicQueryService> : AjaxController
    18         where TDynamicQueryService : IDynamicQueryService
    19     {
    20         /// <summary>
    21         /// 动态查询服务。
    22         /// </summary>
    23         protected abstract TDynamicQueryService QueryService { get; }
    24 
    25         /// <summary>
    26         /// 获取分页数据,面向表格。
    27         /// </summary>
    28         public ActionResult Page(DynamicQueryObject query)
    29         {
    30             var result = this.QueryService.Page(query);
    31 
    32             return this.Json(result);
    33         }
    34 
    35         /// <summary>
    36         /// 获取列表数据,面向不需要分页的表格或下拉框。
    37         /// </summary>
    38         public ActionResult Fetch(DynamicQueryObject query)
    39         {
    40             var result = this.QueryService.Fetch(query);
    41 
    42             return this.NewtonsoftJson(result);
    43         }
    44 
    45         /// <summary>
    46         /// 获取一个数据,面向表单。
    47         /// </summary>
    48         public ActionResult SingleOrDefault(DynamicQueryObject query)
    49         {
    50             var result = this.QueryService.Fetch(query);
    51 
    52             return this.NewtonsoftJson(result);
    53         }
    54     }
    55 }

    万能查询对象

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace Happy.Query
     8 {
     9     /// <summary>
    10     /// 动态查询对象。
    11     /// </summary>
    12     public sealed class DynamicQueryObject
    13     {
    14         /// <inheritdoc />
    15         public DynamicQueryObject()
    16         {
    17             this.Columns = new List<string>();
    18             this.Page = 1;
    19             this.ItemsPerPage = 25;
    20         }
    21 
    22         /// <summary>
    23         /// 表或试图名字。
    24         /// </summary>
    25         public string TableOrViewName { get; set; }
    26 
    27         /// <summary>
    28         /// 表或试图名字。
    29         /// </summary>
    30         public List<string> Columns { get; set; }
    31 
    32         /// <summary>
    33         /// Where子句。
    34         /// </summary>
    35         public string WhereClause { get; set; }
    36 
    37         /// <summary>
    38         /// Order子句。
    39         /// </summary>
    40         public string OrderClause { get; set; }
    41 
    42         /// <summary>
    43         /// 第几页数据。
    44         /// </summary>
    45         public long Page { get; set; }
    46 
    47         /// <summary>
    48         /// 每页条数。
    49         /// </summary>
    50         public long ItemsPerPage { get; set; }
    51     }
    52 }

    备注

    写这篇文章的目的,是系统大家多给些意见,我想知道你们是如何应对这种查询需求的。

  • 相关阅读:
    Git 游离态的一次问题解决
    idea每次新建项目的默认路径
    springboot 整合 freemarker
    Linux 学习网站
    springtask 基本使用和 cron 表达式
    volatile 关键字 和 i++ 原子性
    python 自动补全
    nagios维护之常见问题
    nagios维护之添加监控
    windows下python文件与文件夹操作
  • 原文地址:https://www.cnblogs.com/happyframework/p/3094164.html
Copyright © 2020-2023  润新知