引言

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

在演讲中,他提到了Sandbox、Private Domain、Public Domain和Collaboration Domain等一些概念,为更好地应用DDD开辟了不同视角。以下便是我的思考与收获。

正文

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框架。

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

观《if (domain logic) then CQRS, or Saga?》所悟的更多相关文章

  1. domain logic approaches

    领域逻辑组织可以分为三种主要的模式:事务脚本(Transaction Script).领域模型(Domain Model)和表模块(Table Module)” 1.domain logic appr ...

  2. Domain logic approachs

    1.transaction script(事务脚本) 概述: 很多企业应用可以看成一系列的事务,每一个事务可以通过使用一个Transaction Script来处理. 用法: 使用Transactio ...

  3. CQRS FAQ (翻译)

    我从接触ddd到学习cqrs有6年多了, 其中也遇到了不少疑问, 也向很多的前辈牛人请教得到了很多宝贵的意见和建议. 偶尔的机会看到国外有个站点专门罗列了ddd, cqrs和事件溯源的常见问题. 其中 ...

  4. 关于CQRS(老外经典好文)

    CQRS means Command Query Responsibility Segregation. Many people think that CQRS is an entire archit ...

  5. Introduction to CQRS

    原文链接:  http://www.codeproject.com/Articles/555855/Introduction-to-CQRS What is CQRS CQRS means Comma ...

  6. 翻译:wiki中的business logic词条

    Business logic 业务逻辑 From Wikipedia, the free encyclopedia 来自Wikipedia,自由的百科全书 In computer software, ...

  7. Domain Driven Design and Development In Practice--转载

    原文地址:http://www.infoq.com/articles/ddd-in-practice Background Domain Driven Design (DDD) is about ma ...

  8. 领域驱动设计(Domain Driven Design)参考架构详解

    摘要 本文将介绍领域驱动设计(Domain Driven Design)的官方参考架构,该架构分成了Interfaces.Applications和Domain三层以及包含各类基础设施的Infrast ...

  9. [转载]领域驱动设计(Domain Driven Design)参考架构详解

    摘要 本文将介绍领域驱动设计(Domain Driven Design)的官方参考架构,该架构分成了Interfaces.Applications和Domain三层以及包含各类基础设施的Infrast ...

随机推荐

  1. RedHat操作指令第4篇

    top(查看动态进程运行情况) top 是一个更加有用的命令,可以监视系统中不同的进程所使用的资源.它提供实时的系统状态信息. 显示进程的数据包括 PID.进程属主.优先级.%CPU.%memory等 ...

  2. (四)一个bug的生命周期

    Bug的属性 Bug重现环境 这个应该是我们重现BUG的一个前提,如果没有这个前提,我们可能会无法重现问题,或者根本就无从下手. • 操作系统 这个是一般软件运行的一大前提,基本上所有的软件都依赖于操 ...

  3. Java 中的语法糖,真甜。

    我把自己以往的文章汇总成为了 Github ,欢迎各位大佬 star https://github.com/crisxuan/bestJavaer 我们在日常开发中经常会使用到诸如泛型.自动拆箱和装箱 ...

  4. es6语法糖

    ES6为一些已有的功能提供了非破坏性更新,这类新语法能做的事情其实用ES5也可以做,只是会稍微复杂一些,称之为语法糖. 对象属性的简洁表示法 声明的对象中包含若干属性,其属性值由变量表示,且变量名和属 ...

  5. 【题解】切割多边形 [SCOI2003] [P4529] [Bzoj1091]

    [题解]切割多边形 [SCOI2003] [P4529] [Bzoj1091] 传送门:切割多边形 \(\text{[SCOI2003] [P4529]}\) \(\text{[Bzoj1091]}\ ...

  6. Java集合源码分析(一)——集合框架

    集合框架 集合框架如图所示 Java集合是Java提供的工具包,主要包括常用的数据结构,包括:集合.链表.队列.栈.数组.映射等. 集合的工具包位置是java.util.* 集合主要可以分为五类: L ...

  7. Python 表达式 i += x 与 i = i + x 等价吗?

    Python 表达式 i += x 与 i = i + x 等价吗? 看个例子 a = [1, 2, 3] b = a # 写法一 b += [4] # 写法二 # b = b + [4] print ...

  8. JavaScript:常用的一些数组遍历的方法

    常用的一些遍历数组的方法: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  9. Google Analytics 统计用户点击和每个页面浏览的用户id

    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-805xxx-10"></sc ...

  10. Java基础进阶:学生管理系统数组方式分包源码实现,教师管理系统集合和数组两种方式源码实现,图书馆管理系统源码实现,现附重难点,代码实现源码,课堂笔记,课后扩展及答案

    1.案例驱动模式 1.1案例驱动模式概述 (理解) 通过我们已掌握的知识点,先实现一个案例,然后找出这个案例中,存在的一些问题,在通过新知识点解决问题 1.2案例驱动模式的好处 (理解) 解决重复代码 ...