引言

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. [SWPU2019]Web1 空格过滤用/**/ 注释过滤闭合单引号 imformation_schema.columns/tables过滤 用5.7新特性 或无名注入(此处database()不能用)

    0x00 知识点 二次注入流程分析 二次注入漏洞在CTF中常见于留言板和注册登录功能,简单来说可以分为两个步骤: 插入恶意数据(发布帖子,注册账号),用mysql_escape_string()函数对 ...

  2. mysql中table schema的基本操作

    我们通常对数据库进行的增删插检操作,是针对数据库中的文件.mysql数据库中还有一些表(是view,只能做select操作)记录了现有表的meta data,比如某个column的名字,它的定义是什么 ...

  3. kaggle——Bag of Words Meets Bags of Popcorn(IMDB电影评论情感分类实践)

    kaggle链接:https://www.kaggle.com/c/word2vec-nlp-tutorial/overview 简介:给出 50,000 IMDB movie reviews,进行0 ...

  4. NOI Online #2 提高组 游记

    没 NOI Online 1 挂的惨就来写游记吧,不知道为啥 NOI Online 1 民间数据测得 60 分的 T1 最后爆零了... 昏昏沉沉的醒来,吃了早饭,等到 \(8:30\) 进入比赛网页 ...

  5. 转:minhash

    Minhash算法及其应用 一.引言 MinHash算法属于Locality Sensitive Hashing,用于快速估计两个集合的相似度.最早由Broder Andrei Z. 在1997年提出 ...

  6. 题解-CF643G Choosing Ads

    CF643G Choosing Ads \(n\) 和 \(m\) 和 \(p\) 和序列 \(a_i(1\le i\le n)\).\(m\) 种如下操作: 1 l r id 令 \(i\in[l, ...

  7. STL——容器(Set & multiset)的大小

    1. set.size();  //返回容器中元素的数目 2. set.empty();//判断容器是否为空 empty() 是由一下代码实现的,可以发现,当长度为0时返回 false,以此判断容器为 ...

  8. 五、testNG异常处理

    当程序出现异常或者测试中有异常测试案例可以使他抛出异常 例如:0不可以当做除数,如果将除数设置为0会抛出异常 在testNG上加上 expectedExceptions = ArithmeticExc ...

  9. Spring Session解决Session共享

    1. 分布式Session共享   在分布式集群部署环境下,使用Session存储用户信息,往往出现Session不能共享问题.   例如:服务集群部署后,分为服务A和服务B,当用户登录时负载到服务A ...

  10. 目前市面上比较流行的devops运维平台汇总

    1,spug 1,Spug简介 Spug是面向中小型企业设计的无 Agent的自动化运维平台,整合了主机管理.主机批量执行.主机在线终端.应用发布.任务计划.配置中心.监控.报警等一系列功能.演示地址 ...