目录
DDD领域分析
DDD设计呈现之——四色建模
领域建模实际案例
讲DDD之前,咱们得了解一些基本概念,大家都知道DDD指的是领域驱动设计(Domain-Driven Design),那怎么理解DDD呢?
DDD是一个事件风暴(分类划分),进而知道组织划分(中台)、系统划分(微服务)、代码划分/设计的思想方法。
这么理解可能比较抽象,其实它的本质就是:通过将复杂问题简单化,分而治之,降低复杂度。
DDD的出现,契合了当今流行的微服务架构,微服务架构有个特别重要的命题——如何合理划分微服务,而DDD的战略设计完全可以作为微服务划分的依据。
我们讲DDD,它其实是包括了战略设计和战术设计,那他俩有啥区别呢?
战略设计:业务层面的领域建模,它强调业务领域模型的识别与边界划分,简单来说就是业务设计,与代码呈现无关;
战术设计:工程层面的架构设计与模型设计,具体应用到微服务就是完成微服务设计。
这两者谁更重要呢?我认为,战略设计比战术设计更加重要。
在实际生产中,业务会越来越复杂,代码也会越垒越庞大,如果业务没有经过划分,表设计也没有经过划分设计,最后系统将变成一个庞大的单体应用,叠加需求修改代码的时候将会牵一发而动全身。
如果业务经过战略设计合理去划分,每个业务的业务边界会显得特别清晰,即使我们的代码没有使用DDD的思想去完成搭建,即使我们还是使用传统的MVC架构,也不会影响业务正常运作,战略设计是连接产品需求与开发代码的一道桥梁。
那领域驱动的战略设计应该如何分析呢?我们不需要独自去摸索,总结前辈的一些经验即可:
通用语言:它的作用是定义上下文含义,以便限界上下文定义领域边界
事件风暴:由项目团队、领域专家等多人参与,采用头脑风暴的方式进行用户故事分析,找出并建立领域对象
四色建模:按时间发展先后顺序,识别“追溯单据”作用的“时标”概念,直达业务核心数据;它强调可追溯性与执行效率
限界纸笔建模:回到一百年前,在一个我们没有计算机的年代,我们要做业务设计会用什么方法呢?我们可以用纸和笔画表格并写实例,管理核心领域“恰好够用”的数据,增强数据完整性,避免过度设计
DCI建模:可能DCI我们听得比较少,其实DCI架构与MVC架构的提出者是同一个人。DCI建模通过角色扮演模型使得领域模型易于理解,通过小类大对象的手法避免上帝类的问题;同时它也能解决贫血模型和充血模型之争,使模块更加高内聚、低耦合;当然,DCI建模也可以与四色建模融合使用
那么,领域驱动战术设计应该如何落地呢?我认为,考虑两个大方向即可:
模型设计:比如,使用充血模型还是贫血模型
架构设计:比如,DDD/CQRS、整洁架构、六边形架构、清晰架构、DCI架构……
至此,我们了解清楚了DDD的一些基本概念,那DDD为什么对我们如此重要呢?
我认为,DDD可以指导我们业务设计,指导我们代码落地,使得维护变得更简单。
DDD指导设计:
从业务领域视角划分领域边界,构建通用语言进行高效沟通,降低团队新成员对业务的熟悉成本
在不断交流的过程中,通过提炼领域概念与业务抽象建立领域模型,维持业务和代码的逻辑一致性
通过对领域模型归类与行为分析,保证实现业务的准确性
领域建模比数据库建模更轻更全面,而数据库建模不能完全反映系统的全部特性和需求
DDD指导落地:
不过分依赖系统设计人员的经验背景,指导表设计、代码落地
指导微服务的设计和拆分——划分出清晰的微服务边界、可持续演进的微服务架构
简易维护:
从讨论、设计,到评审、落地,以领域模型进行交流,可作为系统业务的核心载体
以微服务的维度划分限界上下文,服务拆分只需要把相应的限界上下文拆出去即可
减少因业务需求迭代(如:模型变更)带来的维护成本
这就是DDD对我们的重要意义,我们不能只以开发视角去看待业务问题,那样的话就会陷入开发思维陷阱,这对我们的业务成长没有任何帮助。
接下来,咱们来回顾一下,DDD有哪些核心要素——
领域(Domain)
领域是指在特定的范围或边界内要解决的业务问题域,它的核心思想是将业务问题域逐级细为子域/核心域/通用域/支撑域,降低业务理解和系统实现的复杂度。
聚合(Aggregate)
聚合内包括聚合根、实体、值对象,DDD中的聚合是不等同于UML中的聚合的,我们使用充血模型设计领域模型的属性、行为,并识别聚合与聚合根。
值得注意的是,一个微服务最小不要小于一个聚合,避免引入分布式事务的复杂度。
限界上下文(Bounded Context,简称BC)
业务的通用语言有它的业务边界,我们不大可能用一个简单的术语没有歧义地去描述一个复杂的业务领域,BC就是用来细分领域,从而定义通用语言所在的边界。
BC包含一个或多个聚合,按业务领域概念划分到不同BC,对应Java代码层面就是顶层包目录结构。
BC之内高内聚,一个业务行为在BC内尽量使用线程级别的交互,以保证ACID;BC之间低耦合,可作为微服务设计和拆分的依据,当然,微服务的拆分粒度还需要结合企业运维的能力。
BC之间最好采用领域事件进行交互,或者引入“翻译器”(或者说防腐层)进行通讯,保持BC间的松耦合。
DDD的核心要素就这三个,搞懂这三个要素,咱们就可以开始搞很多事情了。
当然,领域建模的一些难点咱们也要有所了解,比如:
领域发现:难点在于领域模型的概念提炼、模型分析与归类;
领域划分:难点在于业务边界和应用边界如何清晰划分,如何把控业务设计的粒度,是自底向上归纳划分还是自顶向下演绎划分;
领域建模:难点在于如何识别聚合、聚合根、实体、值对象,如何确立领域模型之间的关系与核心交互等等。
在我们接到需求的时候,会在脑子里把实现的代码过一遍,这对于简单的CRUD来说并不难,但是涉及到更复杂的业务,一个场景就够我们沟通很久才能说清楚,那怎么办呢?
其实很简单,我们需要一个载体,去把思考的过程沉淀下来,等到产品经理来找我们加需求评估影响范围的时候,新人入职给他讲解业务的时候,通过这个载体就能直观地呈现出来,这就是DDD设计呈现的魅力所在。
这里我以我个人用得比较顺手的四色建模法作为DDD的设计呈现。
四色建模法是对领域模型的一种分析方法论,关注点是领域模型的归类,它是一种呈现方法。
四色原型诞生于90年代,最先由Peter Coad和Mark Mayfield提出[Coad92],然后由David North拓展[Coad95-97],是一种被广泛使用的系统分析方法。
使用四色建模法设计出来的四色图,它所表达的类图是一种包含顺序图的完全动态图,它是立体多维的,有异于完全静态的数据库ER图。
那四色原型具体是哪四色呢?我们一起来看看:
时标原型(Moment-Interval Archetype,简称MI)
表示事物在某个时刻或某一段时间内发生的,如销售订单、客户账单、收款记录等,使用浅红色表示。
PPT原型(Part-Place-Thing Archetype,人/事/物原型,简称PPT)
表示参与扮演不同角色的人或事物,如商品、账户、店铺等,使用浅绿色表示。
角色原型(Role Archetype,简称ROLE)
抽象了一种参与方式,由人或组织机构、地点或物品来承担,如客户、商家、仓储团队、财务组织等,使用浅黄色表示。
描述原型(Description Archetype,简称DESC)
属于资料类型的资源、目录式的种类性质对象,或者可以被其他原型反复使用的,如商品类目、支付方式、方法值对象等,使用浅蓝色表示。
接下来,咱们使用四色建模法来分析领域模型,总共分为四大步:
建立时标原型:寻找需要追溯的事件,根据追溯事件寻找足迹
建立PPT原型:丰富模型,寻找时标原型周围的人/事/物,使它可以更好地描述业务概念
建立角色原型:进一步从中抽象出可以参与到不同流程中去的角色
建立描述原型:把一些信息用描述对象补足
这里咱们需要注意的是,整个过程会穿插着原型之间关系/核心交互的标注。
我们来看一幅电商DDD的四色图案例:
如下图所示,这是财务领域模型和支付中心模型的一部分,这里只描述了业务系统是如何运作起来的,并没有涉及表的具体字段设计,全量的模型图因涉及敏感信息不作详细展示:
领域建模就是这么个建模,这里我提一些设计细节:
粉红色指的是时标原型,是核心业务产生的数据,基本上对应表设计
模型属性不需要体现表的审计字段,比如通用的ID、创建者、修改时间、软删除标识等,模型行为也只需要设计核心行为即可,那种约定俗成的CRUD方法就不需要写出来了,设计要懂得取舍
BC内模型除了依赖、聚合等等连线,可使用箭头连接模型之间的核心交互,跨BC之间的模型使用虚线箭头连接,这里我是结合了DCI建模法(D表示数据,C表示上下文、场景,I表示模型间的交互)
对于表示业务唯一的属性,我使用了加粗展示,再也不用跟别人费劲去解析这些模型是用什么维度去建的了
对于还没上线的属性变动(新增/修改),使用红色标记,因为领域模型图是指导我们业务开发的
限界上下文的划分是一种非常主观的边界划分,为了后续代码能够灵活调整,在Controller的URL设计里不需要加上限界上下文
领域模型图就像代码一样,需要我们长期去维护的,不是说做完设计就不去管了,这一点很重要,微服务负责人一定要有这个意识。