作 者 | 闵大为
阿甘:我也不知道,只是想跑而已。
追随者:感觉这样做是有意义的,而且阿甘也还在前面领跑。
基础能力:相对原子的能力是基础(域)能力,这个可以较好地支持业务定制。由于比较基础,表达的产品能力范围也是很大的。但是,一个完善的产品需要串联的基础能力是非常多的,串联的成本也是非常高的。
平台产品:基础能力的通用性,意味着缺少对场景的理解,缺少了进一步提升生产效率的“基因”。所以在交付的时候,会基于一些高频场景进行抽象,形成平台的产品能力,争取做到“拆箱即用”。业务基于“平台产品”这层进行定制的时候,理解成本会大大减少。
细分领域,培养专业的人、事:因为DDD的核心是要求让各个领域做好理解和封装,把一个业务需求拆解、安放在各自合理的地方,通过这样的分解与沉淀,保证了领域的输入,能够得到长期可持续的发展,形成竞争力。
机制保障,不依赖易变的事物:DDD其实在总结很多通用的技巧和经验,能够让这样的实施更具有确定性。无论是聚合根对领域实体的管控能力、限界上下文的交互策略、领域内核的抽象地位...等等,一旦选择尊奉,确定下来,就能够落在代码结构、组织关系、团队文档中,形成共识,不会因为人员等因素的变化而剧烈波动。
叔孙武叔语大夫于朝曰:“子贡贤于仲尼。” 子服景伯以告子贡,子贡曰:“譬之宫墙,赐之墙也及肩,窥见室家之好;夫子之墙数仞,不得其门而入,不见宗庙之美、百官之富。得其门者或寡矣,夫子之云不亦宜乎!”
因为这不是一个确定性的问题分解,你的设计会被放在显微镜下研究,总能找到各种反例。
而且,我们深知,最佳的实践,一定是做得足够的“软”,对扩展留有设计,能够随着业务发展而迭代,不是一个静态的结果。
实践的个例,难以信服:当我们看到“DDD实践”的时候,可能会发问:这也算DDD?不就是一个正常的服务端框架与方案,也无法涵盖其它的场景或者部门系统。
抽象的理论,觉得空洞:当我们看到“抽象的DDD定义与策略”的时候,可能会发问:这也算DDD?不就是一些软件设计的共识,然后强加了一些名词定义,有些策略与我们手上的系统也并不匹配。
抽象理论:如同抽象的接口一样,“DDD理解”最上面的学习主要是理论定义,比如:聚合根、值对象、资源库、核心域、支撑域等各种定义,这些是易于理解掌握的。
通用实践:如同相对具体的抽象类一样,“DDD理解”中间层次是一些通用原则和技巧,比如:上下文的映射策略、架构的选择等。这些因素是确定的,但需要自主进行取舍与选择,并且需要与时俱进,增量的部分需要自己学习补充。
具体实践:如同具体的类实例一样,“DDD理解”中下面层次是一系列的具体实践,结合各自的业务场景,进行了不同因素的设计、取舍与补充。因为涉及的选项较多,造成最终的选择结果往往是发散的,令人感觉“千人千面”。
“代码抽象层次”中层次关系是比较明确的,且有约束。
“领域驱动理解层次”中无法提供明确的约束,都是多个策略的取舍、一些关键的建议。
可以整体作为1个部分。
可以按竖着的正、负切分2个部分:上面1个(红),下面2个(黄、绿)。
可以按横轴的正、负切分2个部分:左边1个(黄),右边2个(红、绿)。
可以切分成3个部分(红、黄、绿)。
开始的时候,可能只是一个大交易,比如:支付开始的时候只是买卖双方自己协议,也不需要建模。
后面支付发展了,也就独立出来一个域。原来不需要专人维护,后面会渐渐拉出一个团队来承接相关研发。
初始化:按照的经验初步的划分,也可以是行业标准(没有行业标准的时候,就只能靠经验了)。
花费评价:生产、交付过程的人力成本度量,关注理解成本、开发效率、系统稳定性、运维成本等因素。
更优解:在业务发展过程中,计算花费评价,分析影响评价的“好因素”、“坏因素”,进行进一步调整。
调整的内容:其实是匹配生产关系。
调整的原则:追求职责的内聚,精细化分工。
不断调整的原因:业务在发展,内聚的标准也需要与时俱进。
影响较大:很难有勇气去频繁地修改一个核心类。
过于集中:随着方法和逻辑的增多,实体越来越臃肿。
场景较多:很多逻辑并不是正交的,不是if这样else那样的,充满着交集与叠加。
原来get set写法很舒服的本质在于,很多的扩展被放在了业务脚本中,业务脚本虽然千疮百孔,但是是在应用层,远离核心逻辑。底层模型、通用组件等基础逻辑还是比较干净的。
应用DDD的时候,把一些行为下沉到领域层之后,也是要考虑扩展的。如果只关注收口,不关注扩展,那的确是“画地为牢”、“捡了籽麻,丢了西瓜”。
本地部署:线下零售交易为了服务稳定性,期望可以具备本地保存数据的能力。
云上产品:售卖给外部企业的交易产品,成本的要求也不尽相同,期望在云上采购不同的存储服务。
如果“大一统”为主,那么针对关系型存储、KV存储等不同存储进行抽象的时候,就会和“长方形与正方形的问题”一样犹豫:
收益:如果你长期维护,了解里面的特殊性,的确是可以省略一些主体代码,提高开发效率。
代价:但大多数人要做扩展的时候,会感到很多不解,有很多本不需要的适配,充满迷惑。
如果以组合为主,那么可以通过多套模版,更好地进行自主选择。这样分而治之,减少了大家的理解成本,也能独立演化,更能适合存储的能力特性。但是需要沉淀多套理解,往往也缺少人力支持。
“交易域”看上去是负责整个交易过程,可以协调各个域,逻辑上比较合适成为协调者,但是主要还是在管理订单,其它赋予的协调能力,这部分并没有领域实体。
“订单域”看上去只是负责订单本身的服务,不太关心其他域,但是因为订单合约上有着所有合约信息(无论是直接还是间接持有),这意味着,“订单域”本身就有协调的潜力,只是职责看上去不够单一而已。
逻辑上个人是认可纯靠理解作为一个域,毕竟知识本身也是一种资产。
但实际上,没有载体,就做不了特别多事情,包括状态记录,数据服务等,只能辅助,没有核心竞争力,难以生存下来。
对外服务接口应该如何切分?
类似服务之间是否可以共用流程?
业务执行过程如何进一步结构化切分?
......
入口按业务活动切分:服务入口按照业务活动扩展,核心理解并关注用例,什么角色要干什么事情。
流程独立承接与编排:业务活动在到达复用层(如:领域服务、外部服务、业务扩展)前保持独立,各自编排。
借助流程文件编排执行过程:将执行过程切分为执行节点,节点的切分可以以“功能点”、”涉及的域“、“涉及的实体“等为依据。节点间的共同能力下沉到基础能力中。
......
原来以分层架构为主:讲究按层次去看,尽量将能力下沉,进行更多工具复用,积累的是通用组件。
现在以领域为中心:讲究按抽象层次去看,尽量将理解融入到领域核心,进行更多“理解”复用,积累的是业务知识。
对上游提供的能力:通过接口声明,说明能承担的职责,在领域内部进行实现支撑。
对下游的能力依赖:外部服务、业务扩展定制、存储服务都可以作为下游服务看待,通过接口声明服务依赖。
原来更多的是基础能力:数据框架 + DO,不需要理解转换,转换在上游完成,DO也会作为核心模型被上游使用,在采用遵循模型策略的时候,上游完全使用DO作为核心对象进行流转。
现在可以理解为“业务组件“:需要实现领域的存储接口,承担协议转换,将领域对象转换为数据存储对象DO,DO也不会被领域直接理解,需要转换为领域对象再往外透,被领域内核定义了表现。

