你的SOA已经使用了EDA和CQRS吗?

来自2013年4月底的 IDDD Tour 演讲:你的SOA已经使用了EDA和CQRS吗?(What SOA do you have (with extended EDA and CQRS material)),谈论了DDD + CQRS +EventSourcing等面向领域的新思想和新技术如何对传统架构发展。

下面我简要对这篇长达274页的PPT进行大概翻译并阐述自己观点如下:

现在SOA这么多年实践下来,面临的问题:
1.碎片化
2.开发和维护成本很高
3.服务无法被重用。
4.当你认为SOA是整合利器时,它自身成了问题根源。
5.没有人再愿意开发和维护服务
6.系统性能相当差。

老体系的SOA风格如下:
当一个客户希望买到一件裙子时,产生订单的服务将涉及设计部门 客户部门 财务部门和生产部门等,比如检查生产部门是否有类似原料等,然后才能创建一个订单。如下图,注意图中箭头方向,订单服务是主动地去和多个部门的服务交互,如果其中一个子服务没有准备好,将导致业务堵塞等待。



上图中传统的SOA还带来可伸缩性Scalable的问题,难以水平扩展,如果是京东 苏宁这种电商,面对尖峰时刻的巨量订单创建,传统SOA几乎没有解决方案。

而使用EDA事件驱动架构和本地缓存策略,如下图,各个模块子服务只要将自己的状态主动提供给订单服务,而不是订单服务主动去调用这些子服务,顺序倒过来,只能靠事件驱动了,就这么简单。


这一切只是刚刚开始。

让我们回顾过去40年计算机界做了些什么,我们花了很多努力将手工流程搬到了用软件实现,因为信息化过程是逐步的,导致这些系统之间是隔离独立的,很难整合在一起。

这时我们有在数据库层面的整合方案,有在应用层面的整合EAI,使用WebService,使用ESB。但是这些方案都不便宜,不简单。

现在我们有更简单的解决方案。

重温一下SOA四原则:
1.边界是显式的
2.服务分享的是Schema和合约Contract,不是类
3.服务基于策略兼容
4.服务是自治的。

对于一个业务领域,我们难道真的只需要一个领域模型就可以全部代表他们吗?

下图是零售领域的模块图:


对于上图中零售领域,我们不能再用传统领域建模方式或者ER建模方式,结果会得出一个蜘蛛网式的大领域模型,虽然有很多类在里面,但是因为类之间都存在关联,实际还是一个大的领域模型。

对于零售领域,我们可以从不同利益相关者角度看待它,能够得出不同人群眼中的模型。

下图一表达不同人看待零售领域有自己的观点,比如数据库认为数据的生命周期很重要,图二表达使用DDD提供解决方案,每个模块其实是DDD中的有界上下文,模块整合是上下文之间共享实体标识罢了。



由于服务分散在不同系统中,这种自治分离状态为整合带来问题,如果一个大服务需要调用分散在其他服务器上的几个子服务,如何保证整个大服务的事务性?

采取分布式事务吗?XA或两段事务?

分布式事务的问题是性能慢,锁的时间无法预计。锁本身无法伸缩扩展。

那么使用同步通讯(如RMI)等方式?如下图:


所谓服务自治,其实就是松耦合,只有松耦合才能让业务更加灵活,这才是为什么一开始我们需要SOA的关键地方。

所谓松耦合,就是需要好的封装和内聚性。

我们只是希望服务之间分享那么一点点,而不是象前面解决方案,将很多服务捆绑在一起调用,相当于骑压在这些独立的服务之上。

让我们从业务本身界限去重新考虑这些服务,这些服务可能代表不同边界的上下文场景而已,然后一起聚合在用户界面,用户界面看到的是这些不同服务整合,但是不代表我们在服务器后端实现时就是一个整体,而是根据服务自治原则,进行松耦合分离,根据有界上下文进行分离,下图表达界面UI和后面模型系统的关系:



下图是我们使用EDA架构实现的零售领域,一个产品的销售涉及多个部门,多个模块,多个自治的服务,那么谁来协调整个订单销售过程中这些模块之间的节奏呢?

让我们将隐式的显式化吧。

那就是领域事件。

领域事件代表过去发生的事情,可以是CustomerBilled CustomerCreated等待事件。

再下面图二代表领域事件帮助协调多个模块之间的节奏,共同完成订单服务。



下面是CQRS介绍,鉴于本站已经有大量讨论,忽略,从贫血模型一章再开始继续,有平板电脑的可以使用http://www.jdon.com/mobile/45467以类似PPT方式浏览本帖,左右滑动可以翻页。

过去只有UI和数据库的两层架构虽然方便简单,但是对于复杂领域难于扩展。如同一盆意大利肉糊面,面和肉糊在一起。

后来,我们有了更多层,多层架构,比如三或四层,UI +应用层+领域层+数据层,MVC模型也是这种架构,M代表领域模型 C代表应用层,V代表UI。

这种方式问题是贫血模型和事务脚本带来的风险,这正好回答了面向领域设计不流行的原因猜测中问题。这种方式如同多层夹饼。如下图:



失血模型是指只有setter/getter方法的类,如下面:



public class A {

private int id;
private int name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public int getName() {
return name;
}

public void setName(int name) {
this.name = name;
}

}

失血模型的优点,门槛低,普通程序员会使用,所有逻辑在一个类中调用。

缺点:难以测试 ,我们经常说单元测试,如果使用贫血模型,就无法测试,因为贫血模型实际就是将数据库中数据拿到内存中,只有数据没有逻辑,一地鸡毛。
其他缺点还有:贫血模型可能如同没穿衣服光身走路,别人都能看到或访问你的隐私,破坏你内部的逻辑,一致性等等。


富模型是有行为的模型,见这篇讨论:领域模型行为设计

订单作为聚合根实体,其内部高聚合了订单细目OrderLine,这两个对象能够一起创建 修改,删除,生命周期一样,如果订单自身有总价限制,就象发票有总金额限制一样,这就是逻辑一致性,当我们新增订单条目时,都要对这个规则约束校验一次,不至于所有订单条目总和的金额大于订单规定的总金额。这些都要靠订单中方法代码实现。



这篇PPT谈到了命令和事件区别。

命令导致聚合根状态发生变化,然后导致一个或多个事件发生并被发布出去。



2013-06-08 12:13 "@banq
"的内容
后来,我们有了更多层,多层架构,比如三或四层,UI +应用层+领域层+数据层,MVC模型也是这种架构,M代表领域模型 C代表应用层,V代表UI。 ...

现在越来越对所谓的分层架构感到厌倦了,层实在太多了,开发成本太高,每一层单独拿出来都无法重用到其他系统。

真正实用的还是驱动整个系统的核心架构,比如OA的工作流引擎,CMS的模板引擎,CRM的数据模型引擎,这三套系统互相配合基本可以满足大部分客户的需求。

纯粹以技术为核心开发系统,通过核心引擎+扩展插件(配置)的方式满足业务需求的架构不是更加灵活么?

SOA、DDD、CQRS这些思想始终是以业务为核心驱动开发的,思想之间的异同也只是分层方式的差异罢了。层分得再好,遇到新项目还是需要从零开始建模。

现在建筑行业已经不需要从零开始建造楼房了,模块化建筑效率是以前的N倍。模块化的材料都是现成的,不同的楼房只是模块搭配的方式不同而已。

同样的道理,不同的业务逻辑都只是配置文件或者插件的区别。对于没有明细需求的客户(只提出大致的方向),使用核心引擎驱动,可以在4人/天之内完成开发,不含美术效果和需求调研。

Online Ordering System 图中 没有关于 error 的部分

如果某个 Service (Billing, Shipping ) 出现异常了, 怎么处理?

这点是特别关心的!
[该贴被donglangjohn于2013-06-09 15:58修改过]

2013-06-09 15:55 "@donglangjohn
"的内容
如果某个 Service (Billing, Shipping ) 出现异常了, 怎么处理? ...

在有关事务那个章节谈了啊,看这张图中代码,如果是传统同步结构,就必须在代码中反复判断调用子服务是否失败,如果失败再重试,具体重试几次合适不得而知,而使用EDA+cache,这些子服务不是被调用,而是主动发出事件,如果子服务内部出错,就不会发出事件,这种方式巧妙而优雅。



[该贴被banq于2013-06-09 18:31修改过]


那我是否可以这么理解,最后一个处理事件的service--shipping service才会将domain送入底层持久化?

如果不是的话,那可就有麻烦了,前面持久了后面却失败了,不好rockback了 - -!