转载

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

7Fresh是京东第一个线上线下融合落地的零售创新业务模式,店内有大量设备的集成,设备供应商达50多家,针对线下业务的特点,团队独立规划和设计POS收银系统、店内生产系统、加工系统、货架陈列系统、魔镜系统、餐饮系统、自助收银系统等共60多个系统,对接电子价签、电子秤、包装机、无人购物车、门店客流检测等几十种设备。

第一期上线40多个系统,后续又高效上线了其他新增20个系统,短时间内完成了别人口中不可能完成的神话。

京东技术沙龙 】 是由企业信息化部/京东大学技术学院联合京东集团各技术体系共同举办,针对京东研发内部,每月1期,分享前沿技术成果及热点话题。建立“技术交流平台”、促进“技术资源共享”、解决“工作痛点”。

经过小编的整理和7Fresh架构师团队的修订后,为大家呈现,带您一同回顾7Fresh系统从0到1的快速构建之路!

01

系统构建历程

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

7Fresh与京东商城一样拥有一整套的交易系统、一键结算系统,但和线上不一样的是,我们还有很多线下系统,店内的生产、加工、库存管理、餐饮等等。

整个项目从产品调研、架构设计,到第一期上线经过了2个半月的开发。第一期就有40多个系统同时上线,基本能把所有的业务系统运行跑通,之后又增加了很多餐饮类,地推类和线下有关的新需求,现在也依然在落地新的需求中。

02

快速开发的主要原因

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

1、用DDD进行战略设计

  • 分治

  • 界限上下文内的技术无关的通用语言

  • 薄薄的技术集成层隔离业务变化

2、虚拟组织保障设计落地

  • 早期组成虚拟的架构师组织,后期产品也加入

  • 不断摸索方法论和原则

3、其他因素

  • 多团队协同包括成都、武汉、技术拓展等部门

  • 框架和组件积累,之前积累的pop-spring-boot和popdesign及脚手架

  • 业务团队的大量参与,与业务开展同步进行

03

我们对DDD的理解

1、首先是战略部分,这也是我们认为实施比较好的部分:

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

(1)划分和集成界限上下文。

这个虽然不是技术上的问题,但是最关键的还是对业务的理解,跟部门业务专家一起工作很重要,另外是参考了京东现有的情报,做了很多的改进。怎么把系统进行解耦,这个领域边界界定以后,首先上下文最重要的是界定通用语言,就是在一个上下文里边有一套完整明确的概念:

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

一个店就相当于一个仓,WMS开始设计的时候概念方式是管理统一SKU。而我们线下店有做蛋糕、做餐饮这些场景,加工的原材料也应该在系统里,WMS如果强绑定销售商品的SKu的话,那我们线下店就没法管理了。

所以,经验就是不能把一个概念应用超出上下文界定的领域,这样做的好处就是,通过这种方式隔离一些变化,适应更多的场景,实现解耦。

(2)面向服务集成而不要面向数据集成。

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

如果这样的话就能解耦了吗?

随着一些新需求的推进,还是发现里边有些耦合存在。总结的经验就是: 下游的系统应该对需求做抽象,提出自己的标准,这样才能实现解耦。 做DDD领域设计的时候,不应该受到具体架构风格、模式的影响,不管是微服务的还是单体加模块化的风格,最终的效果应该都是一样的,界限分明。

(3)技术无关地考虑领域模型,比如使用类图,定义和功能场景甚至状态图和对象快照。

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

2、使用DDD进行战略设计相对容易落地,收益明显。在使用DDD进行战术设计却遇到了重重困难。

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

战术部分在落实到代码层面遇到了困难。看过业内关于DDD的分享,发现其实大家能把战术层面把DDD落地的,其实都做的不理想。以下是我们对战术层面上的理解:

(1)代码即设计

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

代码即设计,在代码中尽量表现出想要的设计意图和领域意图,但是像性能这类的属性很难表现出来,能够实现但却不能表现意图。而领域设计是比较能在代码层面表现出来的。

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

难点一:聚合根识别困难

难点二:贫血模式。基本我们的框架和思路,很机械式的编码和设计,很难实现上述的目标;

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

难点三:按DDD的理论,聚合根之间不赞成使用数据库事务,成本很高。

(2)用包来体现实体概念

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

左图中间的部分就是领域模型,这也是现在比较通用的方式,领域模型包括领域对象、领域服务,领域对象总是要被存储的,这就需要依赖于仓库,仓库在领域下应该是个接口,我们认为领域应该依赖于技术实现,存储的实现应该在另外一个包下,通过依赖倒置的方式,把领域模型包含的东西和基础设施层分隔。

(3)订单状态变更的例子

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

现在有很多不同类型不同场景产生的订单,它们之间的状态变更是不同的,从未支付到已支付再到在拣货,当中有很多状态是不一样的。如下:

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

从设计层面可以划分不同的状态设计,但是最后状态无非是当前状态,编码实现上就目前来看,写在service层到处都是判断……,来一个新业务就要改一次新判断,但是我们现在从构思想,简单来说先把它放在order对象里,因为这只跟order的数据有关系。

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

从集成的角度来说,如果支付系统接收到支付完成的消息,我们把消息payevent变成一个事件,传给了orderservice-changestate,orderservice里边非常简单,把order从仓库里边加载出来,然后调动方法即可。

大部分业务里的规则还是在orderchangestate方法里。这么做的好处就是,它能够表达出我想要的设计意图,如果分散在其中的话,可能只与设计文档不一致。

第二个就是说,把领域规则识别出来放在对象中会带来额外的好处是,领域规则往往跟外部是没有关系的,很容易做纯粹的自动化的单元测试。

(4)数据一致性和领域事件

一个微服务内部的多个聚合根,可以使用数据库事务保证数据一致性,虽然不完美,但还算实用。用领域事件去做最终一致性成本太高。

两个上下文之间用MQ做最终一致性

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

京东技术   关注技术的公众号

7Fresh 系统快速构建之路:DDD 领域驱动设计实践

长按,识别二维码,加关注

原文  https://mp.weixin.qq.com/s/3Y5zvBLushyhEN6fTE1fiA
正文到此结束
Loading...