一、分离查询命令 Separating commands from queries

    早期的面向DDD设计方法的难点是如何设计一个类,这个类要包含域的方方面面。通常来说,任务软件系统方法调用可以分为两类:查询和命令。在这里,查询是指一个系统的和个操作,它不会改变系统的任务值,仅返回一些结果。命令的职责是个性系统数据。 如果两组方法都使用相同的域模型,逻辑上可能存在查询和命令分离不明显问题,所以引入新的设计模式。

  1. 从域模型到 CQRS

    从某种程序度上,CQRS是复杂的域模型设计的一种横向设计思维。如果域模型客观上就是复杂的,我们使用CQRS是否还需要? 从目前的设计理念看来,CQRS使用的是两个不同的域模型,而不是一个。分离是通过将查询和命令分离到两个层上,它们各有自己的架构体系和服务集合。

  • 查询和命令域层结构

    命令和查询的简单对系统的设计影响可能让人惊讶。系统的组织作为两个并行分支,如图上图 所示的体系结构,有一个正式的域模型是系统的严格要求。

    在提供查询服务的时候我们可能不需要域模型,查询仅仅是用记接口用记查询数据的方法。查询的结果中有可能有命令,所以域模型中设计的各种关系和约束都是没必要的。域模型的查询区域专门定制简单的数据传输类DTO做为数据载体。这种情况下,域服务可能成为使用弱类来实现业务有的某些功能。

  •     CQRS不是顶层设计

    不同 于DDD,CQRS 不是企业级系统设计的综合方法。CQRS是为你在大型系统中为上下文边界设计的一个指导模式。DDD设计基本统一语言,贯穿于整个系统设计。

  • CQRS的优点

        -简单化设计 Simplifcation of the design

     在域模型交互设计中,通常要对面系统的复杂点是改变系统状态的操作。命令需要验证当前状态并决定是否执行,命令同步要保持系统数据的一致性。在读写共享相同的数据实体时很难确保一个操作不会做出意料之外的读写操作。早期我们就认识到模型的命令复杂度以笛卡尔积式增长的。称N为查询和命令的复杂度。在单个域模型中,在查询的规定和约束影响命令和反之亦然,如同笛卡尔积,复杂度的成长为N*N。如果将查询和命令分离,模型的复杂度则为N+N。

        -增强可扩展性的潜力 Potential for enhanced scalability 

    可扩展性有很多方面的因素,针对性解决方法是保持每一个系统的唯一性。通常,可扩展性指系统的可维护性以及在用户增长数量级下的性能。架构 的可扩展性取决于大多数方法的操作类型,如果读取是主要的操作类型,可以引入缓存彻底解决数据库的读取压力。如果是写操作将系统拖慢,应考虑使用异步写替代同步,或使用队列。读写分离后,对系统的可扩展性可以更容易、更有针对性的处理。

  • 在业务逻辑层使用CQRS

        CQRS实际上没有什么负面影响,如何使用CQRS取决你如何理解CQRS,目前来说它只用于一种模式,在两个不同的层,一个是查询服务层一个是命令服务层。系统中所有的部分就会由CQRS带来益处,并且不需要太多的学习成本。

  • 查询堆栈 The query stack

        只读域模型 The read domain model

    一个只用于读的域模型要比读写兼备的域模型简单的多。有下面一个问题,一个Order类中有一个产品类项列表属性。该属性本质上包含可枚举的数据,但不知道Items应该是哪种类型,第一种方法是使用泛型IList<T>,这种方法可以实现:

public IList<OrderItem> Items { get; private set; }

使用ReadOnly属性是更好的先择。Read-only不允许更改集合的结构,此外,如果作为包装器,用于常规的列表创建只读集合,则对基础列表的更改不影响只读包装

public class Order

{

    private readonly IList<OrderItem> _items;

    public Order()

    {

        _items = new List<MOrderItem>();

    }

    public ReadOnlyCollection<OrderItem> Items

    {

        get

        {

            return new ReadOnlyCollection<OrderItem>(_items);

        }

    }

    public void Add(int id, int quantity)

    {

        _items.Add(new OrderItem(id, quantity));

    }

}

public class OrderItem

{

    public OrderItem(int id, int quantity)

    {

        Quantity = quantity;

        ProductId = id;

    }

    public int Quantity { get; /*private*/ set; }

    public int ProductId { get; /*private*/ set; }

}

 

            设计只读模型

    查询堆栈可能仍然需要域服务从存储中提取数据,并为它服务达应用程序和表示层。在这种情况下,域名服务和专门的存储库,应将重定向允许只读取的操作在存储上。在这种情况下,域名服务和专门的存储库应将重定向允许只读取的操作在存储上。

……………………………………

  •  命令堆栈The command stack

    的CQRS场景下,Command是的唯一作用就是改变系统的数据。通常应用层接受来处表现层的数据并执行。命令是针对后端,如注册一个新用户、 处理购物车的内容或更新的客户配置文件等数据落地操作。CQRS 的角度来看,命令就是数据持久化的单向操作。

    任务有两种方式被触发,一种是用户在UI上明确的开始一项任务,别一种是由系统的一些服务自动触发的任务。命令的主要任务是更新系统数据,但有时候调用都需要返回一些数据来确认调用是否成功。

  • 命令与事件

    有两种类型的消息:命令和事件。两种类型中命令是一种数据包,命令是系统执行请求的必要数据。它们有相同点也有不同点

    命令由调用者直接发出

    命令可以被系统驳回

    命令可能会执行失败

    基于网络的命令会依赖系统的当前状态

    事件不能由系统驳回或取消

    事件可以有多外处理者

    The processing of an event can, in turn, generate other events for other handlers to process.

    An event can have subscribers located outside the bounded context from which it originated

    
 

    事件类写法,如下面的代码,命令和事件都继承自Message类。

public class CheckoutCommand : Message

{

    public string CartId { get; private set; }

    public string CustomerId { get; private set; }

    public CheckoutCommand(string cartId, string customerId)

    {

        CartId = cartId;

        CustomerId = customerId;

    }

}

Conversely, here's the layout of an event class.

public class DomainEvent : Message

{

// Common properties

...

}

public class OrderCreatedEvent : DomainEvent

{

    public string OrderId { get; private set; }

    public string TrackingId { get; private set; }

    public string TransactionId { get; private set; }

    public OrderCreatedEvent(string orderId, string trackingId, string transactionId)

    {

        OrderId = orderId;

        TrackingId = trackingId;

        TransactionId = transactionId;

    }

}

 

命令与事件处理

     命令由一个被称为Command bus的处理者来管理。事件由Event bus组件来管理。有此时候命令和事件由同一个bus来处理。下图是基于一个事件的CQRS解决方案。所有的任务都是由用户接口发起,在Asp.net MVC中Controller中的Action接收请求并向应用层发起命令。

 

 

Bus 组件

   Command Bus持有一系统已知业务处理器,这些处理器可以有命令来触发。事件的处理同时会在域中产生许多事件。生成的事件被发布到同一个命令bus或Event bus。Comand Bus是一个接收消息并且找出执行方法的单独的类,Bus类不会自己执行实际要处理的任务,它会选择一个已注册的处理者来处理事件或命令。

public interface IHandles

{

void Handle(T message);

}

接口同时处理命令和事件

public class Bus

{

    private static readonly Dictionary<Type, Type> SagaStarters =

    new Dictionary<Type, Type>();

    private static readonly Dictionary<string, object> SagaInstances =

    new Dictionary<string, object>();

    public static void RegisterSaga<TStartMessage, TSaga>()

    {

        SagaStarters.Add(typeof(TStartMessage), typeof(TSaga));

    }

    public static void Send<T>(T message) where T : Message

    {

        // Publish the event

        if (message is IDomainEvent)

        {

            // Invoke all registered sagas and give each

            // a chance to handle the event.

            foreach (var saga in SagaInstances)

            {

                var handler = (IHandles<T>)saga;

                if (handler != null)

                    handler.Handle(message);

            }

        }

        // Check if the message can start one of the registered sagas

        if (SagaStarters.ContainsKey(typeof(T)))

        {

            // Start the saga creating a new instance of the type

            var typeOfSaga = SagaStarters[typeof(T)];

            var instance = (IHandles<T>)Activator.CreateInstance(typeOfSaga);

            instance.Handle(message);

            // At this point the saga has been given an ID;

            // let's persist the instance to a (memory) dictionary for later use.

            var saga = (SagaBase)instance;

            SagaInstances.Add(saga.Data.Id, instance);

            return;

        }

        // The message doesn't start any saga.

        // Check if the message can be delivered to an existing saga instead

        if (SagaInstances.ContainsKey(message.Id))

        {

            var saga = (IHandles<T>)SagaInstances[message.Id];

            saga.Handle(message);

            // Saves saga back or remove if completed

            if (saga.IsComplete())

                SagaInstances.Remove(message.Id);

            else

                SagaInstances[message.Id] = saga;

        }

    }

}

  Bus的功能就是做命令映射和事件分发。

 

Saga组件

一般情况下,一个Saga组件看起来像逻辑上相关的方法和事件处理程序的集合。每个Saga是一个组件,它声明了以下信息:

  •  命令或启动与Saga组件关联进程的事件
  • 命令Saga组件可以处理和aga组件感兴趣的事件

 

 

 

 

        
 

 

 

 

 

.net架构设计读书笔记--第三章 第10节 命令职责分离(CQRS)简介(Introducing CQRS)的更多相关文章

  1. .net架构设计读书笔记--第三章 第8节 域模型简介(Introducing Domain Model)

    一.数据--行为转变     很长的时间,典型的分析方法或多或少是以下两种,第一,收集需求并做一些分析,找出有关实体 (例如,客户. 订单. 产品) 和进程来实现. 第二,手持这种理解你尝试推断一个物 ...

  2. .net架构设计读书笔记--第三章 第9节 域模型实现(ImplementingDomain Model)

        我们长时间争论什么方案是实现域业务领域层架构的最佳方法.最后,我们用一个在线商店案例来说明,其中忽略了许多之前遇到的一些场景.在线商店对很多人来说更容易理解. 一.在线商店项目简介 1. 用例 ...

  3. 《Linux内核设计与分析》第六周读书笔记——第三章

    <Linux内核设计与实现>第六周读书笔记——第三章 20135301张忻估算学习时间:共2.5小时读书:2.0代码:0作业:0博客:0.5实际学习时间:共3.0小时读书:2.0代码:0作 ...

  4. .net架构设计读书笔记--第一章 基础

    第一章 基础 第一节 软件架构与软件架构师  简单的说软件架构即是为客户构建一个软件系统.架构师随便软件架构应运而生,架构师是一个角色. 2000年9月ANSI和IEEE发布了<密集性软件架构建 ...

  5. 《linux内核设计与实现》读书笔记第三章

    第3章 进程管理 3.1 进程 1.进程 进程就是处于执行期的程序. 进程包括: 可执行程序代码 打开的文件 挂起的信号 内核内部数据 处理器状态 一个或多个具有内存映射的内存地址空间 一个或多个执行 ...

  6. .net架构设计读书笔记--第二章 第7节 神化般的业务层

    一.编排业务逻辑的模式1. 事务脚本模式TS(The Transaction Script pattern ) TS模式概述     TS 鼓励你跳过任何的面向对象的设计,你直接到所需的用户操作的业务 ...

  7. 《Linux内核设计与实现》读书笔记 第三章 进程管理

    第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...

  8. 《CSS3实战》读书笔记 第三章:选择器:样式实现的标记

    第三章:选择器:样式实现的标记 选择器的魔力在于,让你完全实现对网页样式的掌控.不同的选择器可以用在不同的情况下使用.总之把握的原则是:规范的编码,根据合理地使用选择器,比去背选择器的定义有价值的多. ...

  9. .net架构设计读书笔记--第二章 设计体系结构

    第五节 探索领域架构 一.领域驱动设计的价值与意义 最初在java中使用,.net要晚些才引入.领域驱动设计出现之初的争议.一个向导,少走弯路   1. 我们真的需要DDD吗? DDD并不适用于每个软 ...

随机推荐

  1. js开发工具箱

    昨天看到一位大牛的博客,里面有一篇文章“web前端开发分享-目录”,文章中提到的一个给前端er用的一个js开发工具箱.自己使用了一下,非常好用,代码压缩,代码美化,加密,解密之类基本功能都有,生成二维 ...

  2. 【从0到1】android网络框架的选型参考

    项目会使用到 socket tcp 级的网络访问,想选取一个使用较成熟异步网络框架, 提到的网络框架: 1. volley, 2. xutils. 3. android 4. netty, 5. mi ...

  3. C# HttpWebRequest 绝技 转至 http://www.sufeinet.com/

    转至: 在线测试工具http://www.sufeinet.com/thread-3690-1-1.htmlc# HttpWebRequest与HttpWebResponse 绝技    如果你想做一 ...

  4. where,having与 group by连用的区别

    select 列a,聚合函数 from 表名 where 过滤条件 group by 列a having 过滤条件 group by 字句也和where条件语句结合在一起使用.当结合在一起时,wher ...

  5. C语言 数组之无限循环

    #include<stdio.h> #include<stdlib.h> #include<Windows.h> //定于数组的大小 #define N 10 vo ...

  6. [转]开发Visual Studio风格的用户界面--MagicLibrary使用指南

    本文的示例代码为可以从这里下载: 1           概述 微软Visual Studio.NET开发工具推出已经好几年了,这个开发工具一推出就以其易用性和强大功能深受开发者的喜爱.尤其是.NET ...

  7. CSS 实现加载动画之四-圆点旋转

    圆点旋转也是加载动画中经常用到的.其实现方式和菊花旋转一样,只不过一个是线条形式,一个是圆点形式.圆点按照固定的旋转角度排列,加上延时的改变透明度的动画就可以实现.这个实现也比较简单. 1. 动画截图 ...

  8. 2015年新版C#从入门到精通(第2版)视频教学录像【无水印版】

    <c#从入门到精通(第2版)>以零基础讲解为宗旨,用实例引导读者学习,深入浅出地介绍了c#的相关知识和实战技能.<c#从入门到精通(第2版)>第1篇[c#语言基础]主要讲解c# ...

  9. Android Studio Gradle编译项目报错

    Gradle project sync failed Android Studio每次更新版本都会更新Gradle这个插件,但由于长城的问题每次更新都是失败,又是停止在Refreshing Gradl ...

  10. [CareerCup] 13.4 Depp Copy and Shallow Copy 深拷贝和浅拷贝

    13.4 What is the difference between deep copy and shallow copy? Explain how you would use each. 这道题问 ...