本文转载自微信公众号《Java艺术》,作者wujiuye。转载本文请联系爪哇艺术公众号。为什么DDD难以实现?因为我们习惯了安慰,并不是不想接受新事物,而是思维懒惰,习惯了平时的流程开发和面向数据库的CRUD开发,很难改变思维。DDD要求我们基于产品原型进行建模,识别域、限界上下文和子域。这些需要时间去思考的问题就像一座座大山,让我们望而却步。又因为DDD带来的效率在项目前期是看不出来的,不是那么敏捷,前期的建模可能要重做,这让更多人一开始想放弃,但是只有随着需求的不断迭代,DDD才会显示出它的优势。在这篇文章中,作者通过最近的一个项目,来和大家分享一下项目的域识别、限界上下文、子域和建模的过程。当然,分享的只是初始版本。领域通常是指业务范围,每个公司都有自己明确的业务范围。通常每个公司都有很多内部系统。比如一个电商公司可能有物流系统、电商系统、直播系统等,每个系统做的是一个更细分的领域。茉莉红交易所项目是笔者加入茉莉数据集团后做的第一个项目。这也是一个新项目。由于没有太大的历史包袱,笔者选择从头开始构建整个项目。又因为初期业务简单,所以在这个项目中选择了尝试DDD,希望随着业务的不断迭代,能够看到DDD的优势。一开始从产品上了解到这个项目的业务是OTO(onlinetooffline)探店,所以OTO探店是我们要了解的领域。探店其实是一种人才付费活动,帮助商家进行推广。商家让达人免费品尝美食或参观景点,达人最终通过短视频、直播、图文内容等方式宣传商家。无论是探索食品店还是探索游乐园,探索店都是这个领域的核心。在探店领域,核心的业务术语是:商户、专家、店铺、订单、任务。行为包括:商家下单探索店铺,订单关联店铺,商家认证店铺,专家接受订单(任务),下单时商家通知专家,专家在任务完成时通知商家。现在,我们需要为业务划分限界上下文。限界上下文是业务概念的边界,是业务问题的最小粒度划分。OTO门店探索业务领域将存在多个限界上下文。我们通过找出这些确定的限界上下文来解耦系统,并且要求每个限界上下文必须组织严密,职责明确,责任级别高。凝聚。我们划分好的boundedcontext如下图所示。为什么要将任务和订单拆分到不同的限界上下文中(任务不是实体作为订单的聚合根,而是独立聚合的聚合根)?这是因为一个商家发出的一个订单可以让不同的多个master接单,一个master也可以接不同商家的订单,不是简单的一对多关系。这更像是产品到订单的关系,而不是订单到订单的项目关系。限界上下文划分后,还需要根据限界上下文识别问题子域。问题子域是业务问题的划分。与限界上下文相比,它是对业务问题的更细粒度的划分。核心(子)领域:产品的核心竞争力和利润来源;通用子域名:通用,不同领域共享,购买即可使用;支撑子域:非核心域,非通用域,有个性化需求,用于支撑核心域的运行;根据限界上下文,我们划分子域如下图所示。OTO探点核心领域:商户创建订单,平台审核订单,专家收到订单后生成任务,平台审核任务,专家完成任务并填写内容链接;商户支持子域:商户注册、商户评价;专家支持子域:人才注册、人才档案管理、人才粉丝数据提取;店铺支持子域:商户注册店铺、店铺点评;(根据下一版本的要求,店铺将作为商户聚合根的实体)消息通知通用子域:短信通知、应用内通知、小程序消息推送。划分完子域之后,我们就可以对域进行建模了。领域建模是通过将业务抽象为聚合、实体、聚合根和值对象模型,封装和承载所有业务逻辑,从而保持业务的高内聚和低耦合。聚合:负责封装业务逻辑、内聚决策命令和领域事件、容纳实体、聚合根和值对象。聚合根:也是一个实体,是聚合的根节点,比如一个订单;实体:聚合的骨干,具有唯一的标识和生命周期,例如订单项;值对象:实体的附加业务概念,用于描述实体包含哪些业务信息,如订单送货地址。从技术实现上来说,聚合是一个包,里面存储了领域服务、工厂、资源库、聚合根、实体、值对象。领域层包的划分规则通常为:--domain----boundedcontext------aggregation------(聚合根、值对象、实体、领域服务、资源库、领域event)------aggregation--------(aggregateroot,valueobject,entity,domainservice,resourcelibrary,domainevent)特别说明,一个限界上下文可能包含多个聚合,但只有一个聚合可以存在于有界上下文中。如果限界上下文只有一个聚合,在这种情况下我们通常会省略限界上下文层。以orderboundcontext和taskboundcontext为例:--domain----ordercontext---orderType(ordertypeaggregation(特殊聚合,用于管理订单分类):food(breakfast/lunch/dinner/Afternoontea),...)---order----task因为order有两个聚合,我们没有省略order有界上下文层,而task只有task聚合,所以省略了task有界上下文这一层.以上包的划分只是领域层的划分,要求聚合根、值对象、实体、领域服务、资源库、领域事件等类都存放在聚合包下,是否使用DDD经典四-层架构或六角架构。我们没有采用经典的DDD四层架构,也没有采用六边形架构。我们对项目包的实际划分如下。当我们需要将order和task根据限界上下文拆分成两个微服务时,只需要复制一份项目代码,去掉一个项目的ordercontext包,去掉一个项目的task包,连接两个boundedcontext应用layers依赖调用改为远程RPC调用。上图中的xxxGateway类用于封装远程调用。UserApplicationServiceGateway和RabbitmqConfiguration之所以放在最外层,是因为这两个boundedcontext都会用到。以任务聚合为例,展开后的包结构如下。以上就是我们对OTO门店探索业务的识别限界上下文、拆解子域、领域建模的初步过程。按照现在的需求进度,我们即将推翻这个模式,重新开始,但是代码的改动应该不会太大。由于DDD缺乏权威的实践指导和代码约束,只能通过实践慢慢积累经验。
