当前位置: 首页 > 科技观察

说说DDD,你学会了吗?

时间:2023-03-20 19:29:27 科技观察

DDD近年来越来越流行。每个人都在谈论这个话题,但是每个人对它的理解不同。这里的小王是基于之前系统拆分、需求评估、遗留系统改造的经验。有了一点经验,说说我对DDD的理解。下面从认知定义、功能、领域建模方法、实现方法论等方面来谈谈。认知定义DDD是一种处理高度复杂领域的设计思想。它试图将技术实现的复杂性分离出来,围绕业务概念构建领域模型来控制业务的复杂性,从而解决软件难理解、难演化的问题。DDD不是一种架构,而是一种架构设计方法论。它通过边界划分简化复杂的业务领域,帮助我们设计清晰的领域和应用边界,轻松实现架构演进和微服务实现。优点:1.作为微服务定义的指导思想。2.了解业务的方法论,在接管遗留系统和改造遗留系统时可以快速了解业务。3.解决领域知识碎片化,代码业务语义薄弱的问题。4.控制系统的复杂度和代码量。缺点:1.DDD不能解决大部分性能优化问题,甚至在大多数场景下,我们都需要做抗DDD设计来进行性能优化。2、DDD解决不了开发技术水平的问题。3、DDD需要我们在领域建模上花费大量的时间和精力,也可能导致付出与收益不成正比的情况。领域建模方法领域建模解决问题领域建模的目的是为了统一大家的业务认知,让业务、开发、测试、产品在同一个通道上进行沟通。事实上,要做到这一点是非常困难的。开发者喜欢从技术层面描述问题,产品习惯于从业务层面描述问题。两人不在同一个频道怎么能沟通好。在小团队中,这种优势无法体现。在一个大团队中,沟通成本非常高。领域建模说起来简单,但要做好真的很难。构建几个实体对象无法解决复杂的需求。仅在脑海中建模在现实中不一定正确和稳定。因此,我们需要找到合适的方法来帮助分析业务领域,获取建模结构,共享建模结果。好在前辈大师总结了一些建模方法。常用的建模方法有:用例分析法、四色建模法、事件风暴法。我不会一一重复。网上有很多内容,或者公众号回复[DDD]获取相关信息。实现方法论战略设计:战略设计主要从业务角度出发,建立业务领域模型,划分领域边界,建立通用语言的有界上下文。限界上下文可以作为微服务设计的参考边界。各域:核心域、支撑域和通用域的主要目标是通过域划分,区分公司不同子域的不同功能属性和重要性,使公司可以针对不同的领域采取不同的资源投入和建设。子域策略,它们的侧重点会有所不同。统一语言:统一语言提供了更好的协作方式的可能性。统一的语言和背后的领域模型赋予了研发人员通过重构定义业务的能力。在业务方多为强势的环境下,很少建立技术反馈给业务方的途径,减少知识消化过程中的失败风险。图片来源:《如何落地业务建模》限界上下文:限界上下文是微服务设计和拆分的主要依据。在领域模型中,如果不考虑技术异构、团队沟通等其他外部因素,理论上可以将限界上下文设计为微服务。限界上下文的定义是:用于封装通用语言和领域对象,提供上下文环境,保证领域中的一些术语、业务相关的对象等(通用语言)具有准确的含义,没有歧义。就像电子商务领域的商品一样,商品在不同的阶段有不同的术语。它们在销售阶段是商品,在运输阶段成为商品。同一个事物,由于不同的业务领域,赋予了这些术语不同的含义和职责边界,而这个边界可能会成为未来微服务设计的边界。看到这一点,领域边界是通过有界上下文来定义的。战术设计:战术设计从技术角度出发,着眼于领域模型的技术实现,完成软件开发和架构实现,包括:聚合根、实体、值对象的代码逻辑和代码分层设计与实现。它主要讨论如何在服务内划分和组织代码。EntitiesandValueObjects:实体和值对象:从领域模型的基本单元看系统设计实体和值对象是构成领域模型的基本单元。实体的代码形式在代码模型中,实体的形式是实体类,实体类包含实体的属性和方法,通过这些方法实现实体本身的业务逻辑。在DDD中,这些实体类通常采用拥塞模型,与该实体相关的所有业务逻辑都在实体类的方法中实现,跨多个实体的领域逻辑在领域服务中实现。实体以DO(DomainObjects)的形式存在,每个实体对象都有一个唯一的ID。我们可以多次修改一个实体对象,修改后的数据可能和原来的数据有很大的不同。但是,由于它们具有相同的ID,因此它们仍然是相同的实体。实体的数据库形式当领域模型映射到数据模型时,一个实体可能对应0个、1个或多个数据库持久化对象。在大多数情况下,实体和持久对象是一对一的。在某些场景下,某个实体只是一个运行实体,暂时驻留在静态内存中,不需要持久化。值对象值对象比实体更抽象。简单来说,值对象本质上是一个集合。值对象的代码形式值对象在代码中有两种形式。如果值对象是单个属性,则直接定义为实体类的一个属性;如果值对象是属性集合,则设计为Class类。类将具有全局概念的多个属性收集到属性集合中。这样一个值对象没有ID,就会被实体作为一个整体来引用。图片来源:《DDD 实战课》例如上图:人物实体原本包括姓名、年龄、性别、人物所在的省、市、县、街道等属性。这样一来,显示地址相关的属性就很零散了是吧?现在,我们可以把“省、市、县、街道等属性”取出来,组成一个“地址属性集合”,这就是值对象。聚合和聚合根:领域模型中的实体和值对象就像个体,使实体和值对象协同工作的组织是聚合,用于保证这些领域对象在实现通用业务逻辑时能够保证数据的完整性.一致性。聚合是与业务和逻辑密切相关的实体和值对象的组合。聚合是数据修改和持久化的基本单位。每个聚合对应一个仓库,实现数据持久化。聚合有一个聚合根和上下文边界(一个聚合包含多个实体对象和值对象,其中一个实体对象作为聚合根。这些对象聚集在一起形成一个相对完整和独立的业务边界,称为上下文边界。),这个边界按照单一业务职责和高内聚的原则定义了聚合中应该包含哪些实体和值对象,聚合之间的边界是松耦合的。这样设计的微服务自然是“高内聚、低耦合”。我们以保险业务场景为例,看看聚合过程涉及到哪些步骤:图片来源:《DDD 实战课》聚合根聚合根叶有属性、值对象、关联实体、自身的业务行为。Leave实体采用充血模型,有自己的业务行为,具体是聚合根实体类的方法,如代码中的getDuration和addHistoryApprovalInfo。聚合根指的是实体和值对象。它可以在聚合中组合多个实体,在聚合根实体类方法中完成复杂的业务行为。这种复杂的业务行为也可以在聚合域服务中实现。但是,为了明确职责和边界,我建议聚合应该根据自身的业务行为在实体类方法中实现,而只有多个实体组合才能实现的业务能力应该由领域服务来完成。下面是聚合根叶子的实体类方法,它包含属性、对实体和值对象的引用,以及它自己的业务行为和方法。publicclassLeave{Stringid;申请人申请人;审批人;离开类型;Status状态;日期开始时间;日期结束时间;持续时间长;()返回endTime.getTime()-startTime.getTime();}publicLeaveaddHistoryApprovalInfo(ApprovalInfoapprovalInfo)if(null==historyApprovalInfos)historyApprovalInfos=newArrayList<>();this.historyApprovalInfos.add(approvalInfo);返回这个;}publicLeavecreate(){this.setStatus(Status.APPROVING);this.setStartTime(newDate());returnthis;}//其他方法}DDD分层架构的最后一个问题是如何组织代码。这时候你就需要去到DDD的分层架构。那么之前的MVC三层架构是如何演变为DDD分层架构的呢?DDD分层架构与MVC架构的映射关系:层与层之间的关系在书中也有描述《领域驱动设计——软件核心复杂性应对之道》:但是小王认为代码的组织方式可以根据团队的情况进行调整,如只要能满足领域驱动的思想就行了。各个层级的作用可以参考之前的文章:领域驱动实现,这里不再赘述。小结本文主要从DDD是什么、能做什么、不能做什么、怎么做(领域建模方法、实现方法论)等方面来谈谈领域驱动。当然,一千个人对域驱动有一千种理解。