领域驱动设计(DDD)研究

网友投稿 924 2022-05-30

1      【引言】

领域驱动设计(Domain-driven design,DDD)是一种针对复杂需求的软件开发方法,通过将实现与不断发展的模型连接起来,从而满足复杂的需求。

2      【领域驱动设计(DDD)】

领域驱动设计基于以下目标:

l  将项目的主要重点放在核心领域和领域逻辑上。

l  将复杂的设计建立在领域模型的基础上。

l  启动技术专家和领域专家之间的创造性合作,迭代完善解决特定领域问题的概念模型。

2.1    概念

领域驱动设计的概念包括:

l  背景上下文

一个词或语句出现的环境,决定了这个词或者语句的意义。

l  领域

一个知识(本体)、影响或活动的领域。用户实施程序部署的主题领域即为软件的领域。

l  模型

模型是一个抽象的系统,它用来描述了一个领域的一些特定方面,可以用来解决与该领域相关的问题。

l  共识语言

要培养出围绕领域模型结构的共识语言,所有团队成员都会使用这种共识语言进行沟通,这会贯彻到团队的各种活动当中。

2.2    战略性的领域驱动设计

在理想的情况下,最好有一个单一的、统一的模型。当然这是不现实的,在现实中,通常会设计出针对不同业务分片的多种模型。认识到这一点,实事求是的解决问题是正确的方向。

战略设计是一套保持模型的完整性、提炼出领域模型,并让多个模型协同工作的一系列原则。

2.2.1    上下文语境的限定

在任何大型项目中,都会有多个模型。然而,当基于不同模型的代码组合在一起时,软件就会变得问题多多、不可靠和难以理解。团队成员之间的沟通也会变得混乱。通常不清楚在什么情况下应该或者不应该使用某个模型。

因此,明确定义模型使用的上下文显得尤为重要。要明确在团队组织、应用程序特定部分、以及代码库和数据库定义设计等物理表现形式等方面设置界限。在这些界限内严格保持模型的一致性,并要确保这个一致性不要被来自外部和内部的问题所分散或混淆。

2.2.2    持续集成

当几个人在同一个有界限的环境中工作时,模型很容易出现碎片化的趋势。团队越大,问题就越大,有时候三四个人也会遇到严重的问题。如果任其发展,系统会分解成越来越小的上下文语境,最终就会失去整合性和连贯性。

因此,应该建立一个快速合并代码的流程,通过自动化测试来快速发现碎片并解决。在各个团队成员的脑海中,随着概念的演变,锤炼出一个共同的模型观点,从而在本项目开发中形成一种共识。

2.2.3    情境图

在没有全局观的情况下,会有个别有界限的上下文会存在一些问题,而其他模型的上下文可能仍然是模糊的,或者处于变化之中的。

一个典型的情况是,当其他团队中的成员不清楚上下文的界限时,会不知不觉中做出一些改变,使得界限边缘模糊化或相互之间的联系复杂化。当这类上下文之间建立联系时,它们往往会相互渗入,增加相互间的耦合性。

因此,确定项目中每个模型的作用,并定义其边界上下文显得尤为重要。 这也包括非面向对象子系统的隐含模型。 给每个受限上下文命名,并使其名称成为共识语言的一部分。描述各模型之间的接触点,概括出任何通信的内容并要通俗易懂,并且强调共享理念。把这些点,模型连起来以后,就可以绘制出现有项目的情景图。

2.3    搭积木

在《领域驱动设计》一书中,阐述了一些高层次的概念和实践,如共识语言是指领域模型应该形成一种由领域专家给出的描述系统需求的通用语言,这种通用语言的使用对业务用户或项目经理和软件开发人员同样适用。书中非常注重将域层描述为面向对象系统中的多层体系结构的通用层之一。在DDD中,表示、创建和检索领域模型的神器有:

2.3.1    实体对象

一个实体对象不是由其属性来定义的,而是由其延续性和特性来定义的。

举例说明:

大多数航空公司在每个航班上都会对每个座位进行唯一的区分。在这种情况下,每个座位都是一个实体对象。

然而,有些航空公司如西南航空、易捷航空和瑞安航空并不区分座位,所有的座位都是一样的。在这种情况下,座位实际上是一个值对象。

2.3.2    值对象

值对象是一个包含属性但没有概念特征的对象。可被视为不可更改的对象。

举例说明:

当人们在交换名片时,一般不会区分每张名片是否不同,而只关注名片上印有的信息。在这种情况下,名片就是价值对象。

2.3.3 集合

由一个根实体绑定在一起的对象集合,又称集合根。集合根通过禁止外部对象对其成员进行修改,从而保证了在集合内更改的一致性。

举个例子:

当你驾驶汽车时,你不必担心车轮向前还是向后移动,发动机是否燃烧燃油等,你只需驾驶汽车就行了。在这种情况下,汽车是其他几个对象的集合体,并作为集合根的形式呈现。

2.3.4    领域事件

一个域对象会定义发生的事情为事件。领域专家通过领域事件来了解领域内正在或者已经发生的事情。

2.3.5    服务

当一个操作在概念上不属于任何对象时。按照问题的业务属性,可以在服务中实现这些操作。

2.3.6    储存库

域对象由一个专门的储存库对象来管理,可以通过其方法进行检索,这样可以方便地交换其他存储库实现。

2.3.7    工厂

创建域对象由一个专门的Factory对象来进行,这样可以很容易地互换多种域对象的替代实现。

2.4    劣势

为了确保模型作为一种纯粹的、有用的共识语言构造,团队成员通常必须在领域模型中实施大量的隔离和封装。因此,基于域驱动设计的系统会付出相对较高的成本。虽然领域驱动设计提供了许多技术上的好处,比如可维护性的提高,但是微软建议只在复杂的领域上实施这种方法。在复杂的领域中,在复杂信息的交流中,模型和共识语言处理和对领域共识的形成上有明显的好处。

2.5    关联观点

2.5.1    面向对象的分析和设计

虽然从理论上讲,DDD的一般思想不需要局限于面向对象的方法,但在实践中,DDD试图利用面向对象技术所带来的优势。这些优势包括实体/集合根作为命令/方法调用的接收者,以及在最重要的集合根中使用封装,在更高的架构层面上,甚至封装有边界的上下文。

领域驱动设计(DDD)研究

2.5.2    模型驱动工程(MDE)和模型驱动架构(MDA)

虽然DDD与MDA/MDE兼容(MDE可被视为MDA的超集),但这两个概念的意图有些不同。MDA更关注的是将模型转化为不同技术平台的代码的手段,而不是定义更好的领域模型。而MDE提供的技术(建立领域模型、建立DSL以方便领域专家和开发者之间的交流...........)则促进了DDD在实践中的应用,帮助DDD实践者从模型中获得更多的收益。得益于MDE的模型转换和代码生成技术,领域模型不仅可以用来表示领域,还可以生成实际的软件系统,用于管理领域。这张图是DDD和MDE结合的可能的表示方式。

2.5.3    普通的旧Java对象(POJOs)和普通的旧CLR对象(POCOs)。

POJO和POCO是技术实现概念,分别是Java和.NET框架所特有的技术实现概念。然而,POJO和POCO这两个术语的出现反映了一种越来越多的观点,即在这两个技术平台的背景下,领域对象的定义应该纯粹是为了实现相应的领域概念的业务行为,而不是由更具体的技术框架的要求来定义。

2.5.4    干脆的对象模式

如果你有一个足够好的领域模型,那么用户界面可以简单地成为这个领域模型的镜像;

如果反过来,你要求用户界面直接反映你的领域模型,那么你就需要设计出一个更好的领域模型。

2.5.5    特定领域建模(DSM)

DSM是通过使用特定领域的语言来践行DDD理念。

2.5.6    特定领域语言(DSL)

虽然DDD并不特别要求使用DSL,但它可以用来帮助定义DSL以及支持特定领域的多模化等方法。

2.5.7    面向外貌的程序设计(AOP)

AOP可以很容易地从领域模型中剔除技术问题(如安全、事务管理、日志),因此更容易设计和实现纯粹关注业务逻辑的领域模型。

2.5.8    命令查询责任分离(CQRS)

CQRS是一种将读和写分离的架构模式,前者是Query,后者是Command。命令会突变状态,近似于对集合根/实体的方法调用。查询是查询状态,但不突变状态。CQRS是从Bertrand Meyer提出的命令查询分离(CQS)设计模式中衍生出来的架构模式。虽然CQRS不需要DDD,但领域驱动的设计围绕着集合根的概念,将命令和查询之间的区别显式化。其思想是,一个给定的集合根有一个对应于命令的方法,命令处理程序在集合根上调用该方法。集合根负责执行操作的逻辑,并产生一些事件(Event Sourcing (ES))或失败响应(异常或执行结果),而不是仅仅改变持久化器实现的状态,如使用ORM写到数据存储,而命令处理程序则负责拉入与保存集合根的状态或事件相关的基础架构,并创建所需的上下文(如事务)。

2.5.9    事件源(ES)

这种架构模式保证你的实体(按照Eric Evans的定义)不通过直接序列化或O/R映射来跟踪其内部状态,而是通过在事件仓库中方读取事件和提交事件到事件仓库的方式来跟踪。当ES与CQRS和DDD相结合时,集合根负责验证和实施命令(通常是通过命令处理程序调用它们的实例方法),然后发布一个或一组事件,这也是集合根处理方法调用的逻辑基础。因此,输入是一个命令,输出是一个或多个事件,这些事件被事务性地(单次提交)保存到事件仓库中,然后发布到消息代理上,供兴趣方使用(通常是视图即为有兴趣方;使用Query-messages查询)。当把你的集合根建模为输出事件时,你可以更好的隔离内部状态,这比从你的实体中投射读数据更进一步,就像标准的n层数据传递架构。这样做的一个重要好处是,像公理证明器(如Microsoft Contracts和CHESS)这样的工具更容易使用,因为集合根全面隐藏了它的内部状态。事件通常是基于集合根实例的版本来持久化的,这就产生了一个在分布式系统中围绕优化并发概念的同步的领域模型。

2.6    相关工具

DDD理念并不依赖于使用任何特定的软件工具或框架。尽管如此,越来越多的应用程序为DDD倡导的特定模式或DDD的一般方法提供了支持。著名的工具和框架包括:

l  Actifsource是Eclipse的一个插件,它可以将DDD与模型驱动的工程和代码生成结合起来进行软件开发。

l  CubicWeb是一个完全由数据模型驱动的开源语义网络框架。高层指令允许通过一个又一个版本的发布迭代完善数据模型。在该工具中,定义了数据模型就可以得到一个正常运行的Web应用程序。当默认的视图不够用时,还需要进一步的工作来定义数据的显示方式。

l  OpenMDX:开源的、基于Java的、支持Java SE、Java EE和.NET的MDA框架。OpenMDX与典型的MDA框架不同,它 "使用模型直接驱动运行系统"。

l  OpenXava: 从JPA实体中生成一个AJAX应用程序。你只需要编写领域类就可以获得一个可使用的应用程序。

l  Restful Objects是一个Restful API到领域对象模型上的标准(其中领域对象可以代表实体、视图模型或服务)。两个开放源码框架(一个用于Java,一个用于.NET)可以使用反射从领域模型中自动创建一套Restful Objects API。

3      参考

https://airbrake.io/blog/software-design/domain-driven-design

https://martinfowler.com/tags/domain%20driven%20design.html

https://medium.com/@jpdeffo/domain-driven-design-ddd-in-microservice-architecture-for-nutshell-19c7c579009a

https://medium.com/code-thoughts/what-i-understand-about-domain-driven-design-f7fbd00e364f

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/ddd-oriented-microservice

https://en.wikipedia.org/wiki/Domain-driven_design

软件开发

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:全面详细解析invoke方法的执行和使用
下一篇:ARM故事:起源、历程和发展
相关文章