观《if (domain logic) then CQRS, or Saga?》所悟

引言

Udi Dahan曾在2017年阿姆斯特丹的DDD欧洲年会上发表过一篇演讲——if (domain logic) then CQRS, or Saga。视频是UP主从Youtube搬运的,我听力水平通常,因此如下内容有所偏颇的话,还请见谅。git

在演讲中,他提到了Sandbox、Private Domain、Public Domain和Collaboration Domain等一些概念,为更好地应用DDD开辟了不一样视角。如下即是个人思考与收获。github

正文

Udi用级联删除的例子,引出了沙盒Sandbox。错误的级联删除操做,特别是在数据库中Table级别上的删除操做,对任何一个系统而言均可谓灭顶之灾。因此瓜熟蒂落的,咱们会使用给数据行打上某种删除标志的方法,称之为“软删除”,避免数据被完全删除。这就象操做系统里的回收站同样,给了咱们一次反悔的机会。若是在全部的软件系统里都有这样一个安全沙盒,是否是就彻底解决了删除问题?数据库

  • 对此,我认为,对于系统中的全部删除操做,首先都应该有领域行为上的具体含义,好比解雇员工、取消订单、停产产品等等。其次,还要考虑受到删除操做影响的数据,要限定在哪一个范围,它们是否有留存下来的业务价值。好比解雇员工后,与之相关的薪酬记录就不能删除,不然会致使帐面错误。反之,只在薪酬记录里有个员工ID,却查不到这名员工的姓名、职务等具体信息,也会发生错误。再或者,这名员工先被裁人,以后在招聘时被优先聘用时,如何处理其原有信息,等等。因此,“删除”操做不能简单论之,必须是从业务领域的角度去思考和命名——其到底是何种业务操做,这种操做在业务上应致使何种后果。好比雇员被解雇后,其全部数据被冻结,其雇员信息再也不会更新,其薪酬不会再发放。

随后,Udi用博客做为例子,引出了Private DomainPublic Domain的概念,并阐述了Sandbox与两者之间的联系。一篇随笔被正式发布前,博主能够在编辑页面随便折腾。而在随笔正式发布后,全部的修改和删除操做就须要慎重了。此处,发布前的随笔处于Private Domain,发布后则进入了Public Domain。对处于Private Domain范围内的私有数据,咱们能够随意地增删查改,而不用顾忌任何的业务规则和约束。而当私有数据被推送到Public Domain时,则必须顺利经过各类规则的审核与验证,进而成为构成系统的固定组成部分。其中,每一个Private Domian对应一个Sandbox,用户能够随心所欲,而没必要担忧对系统形成实质影响。安全

  • 对此,我认为,每一个Sandbox都对应某个具备领域含义的实体,并能够用与之相关的领域事件做为Private Domain与Public Domain的划分界线。好比一份菜谱,你能够随意修改菜谱里各类菜品的价格、名称,甚至新增或者撤掉某个菜品。而在正式提交更新前,全部人仍旧会使用旧的订价和菜名。这里的菜谱,就是全部菜品所在的Sandbox边界。而最终MenuUpdated事件的触发,才标志着操做对系统产生了实质性改变,数据已经进入Public Domain。若是没有这个提交的步骤,让全部的增删查改都当即产生效果,那顾客必定会开骂了。另外一方面,若是没有暂存修改结果的这个Sandbox,也意味着彻底不给饭店老板活路——要改就一口气改完,还不能自相矛盾。回到前述的删除操做也是同理。当数据还在Private Domain时,咱们能够放心地删除之。而当数据已经进入Public Domain后,就必须去发掘删除操做背后的领域含义了,并由此衍生出更复杂的领域逻辑和领域行为。

Udi接着商品被删除的例子,延伸到购物车中已加入的商品售罄或被停售的状况,提出Collaboration Domain的概念。在竞态条件(即并发条件下的竞争条件)下,即代表有多个参与者须要对同一个数据进行操做,此时属处的领域即Collaboration Domain。这样的协做领域,一般能够围绕if语句进行发掘。有if判断涉及其余的实体,则一般代表该数据也可能会被其余参与者改变。此时,CQRS成为很天然的选择。由于命令执行的环境是通过事先检验的,因此命令老是能成功执行。在这样的设定下,把一个用户下单的操做分割为多个步骤,在放入购物车和提交订单时分别进行一次商品有效性的检验。并发

  • 对此,我认为,引入CQRS是由于将命令与查询分离后,检验执行环境的部分将由一组查询构成,改变系统状态的部分则由一组命令组成,这样将更容易发现竞态条件,从而作出合理的选择。

为了减小检验的次数,Udi借Shopping Cart Timeout的例子引出了Collaboration Timeout的概念。给购物车一个活动状态的超时设定:用户放入商品时,购物车进入激活状态,跨入协做领域,此时因商品在售,订单能够成功提交;用户未在超时前提交订单的,购物车进入失活状态,退出协做领域,以后商品将因售罄或停售而没法再加入购物车。框架

  • 对此,我认为,此时的购物车更象是一个愿望清单(Udi在演讲里也有说起),这样的Timeout与超卖都是解决最终一致性条件下的不一样解决方案。MSDN CQRS Journey第164页提到的方案,是根据代价大小,选择对客户作出赔偿或者在付款前加锁断定,让其余人等待一小段时间。当席位较多时,由于竞争风险小,能够将席位是否够用的查询交付异步执行,使用户等待时间减小;当席位紧张时,容许提交失败的用户修改订单。惟品会采用的方式,是在商品加入购物车后,将该商品暂时锁定,保证用户在超时前能成功下单。

因为Collaboration Domain和CQRS的存在,Udi指出,必须改变传统的思考方式,由于Saga将更广泛地出如今模型之中。对此不能有退缩和犹豫,应当与领域专家深刻交流,使本身也成为业务专家,确保每一个流程都彻底可控。最后,Udi提出消息中间件是实现Saga的重要工具,因而顺手推介了一下本身维护的NServiceBus框架。dom

  • 对此,我认为,以用户购物这个Saga为例,将由用户注册->查看商品详情->放入购物车->提交订单->完成支付等多个流程协做完成,而不是简单地放在一个或几个数据库事务里完成。在不一样聚合之间以消息或事件为纽带,是处理Saga的核心与关键。因此CQRS、Event Sourcing以及Message Queue都将是实现DDD的利器。
相关文章
相关标签/搜索