本文转载自微信公众号《木小农》,作者木小农。转载本文请联系沐小农公众号。1.前言最近公司召开了领域驱动设计技术分享会,主要讲解了服务的划分,RestfulAPI的设计,如何抽象出统一业务类的Model,使其模块化,以及在同时能够提炼和组合多个在软件开发中如何降低系统的复杂性是一个永恒的挑战。过去,使用一系列设计模式或示例来降低一些更常见的复杂性。这些都是通过技术手段解决技术问题,并不能从根本上解决业务问题,但是在EricEvans2003年的《Domain Driven Design》中,真正从业务的角度出发,提供了一套纯业务开发的架构思路。为了更深入的了解,看了很多资料,对DDD有了一点个人体会,所以整理出来分享一下,希望对大家有所帮助。2.什么是DDDDDD是EricEvans在2003年出版的书名,也是这种架构设计方法名称的由来。EricEvans“领域驱动设计之父”,全球杰出的软件建模专家。他创立的“领域语言”公司致力于帮助组织创建与其业务密切相关的软件。DDD不是一套架构,而是一种架构思想,导致在代码层面缺乏足够的约束。所以DDD在实际应用中的门槛比较高,在大部分公司的实际应用中并没有应用。是的,或者只适用于DDD的一些思想,比如:建模的思想,整个架构体系的思想无法实现,一些还很火的ORM工具(Hibernate)促成了泛滥模型的贫血,也是因为传统的Database技术和MVC的40%架构应用(UI,Business,DataAccess,Database)仍然可以为我们解决大部分的应用开发。之前的服务架构仅限于单机+LB。MVC提供的Rest接口用于对外提供服务调用,或者使用WebService进行RPC调用。2014年,SOA大行其道,微服务开始如雨后春笋般涌现。按应用或项目对多个微服务进行合理拆分成为了各个技术负责人思考的重点,而DDD中的BoundedContext为我们提供了一套合理的架构思路。但是DDD让我们思考在我们的项目中哪些可以通过服务来拆分,哪些业务逻辑需要聚合在一起才能达到最小的开发和维护成本。3.领域驱动设计-基本概念DDD的全称是DomainDrivenDesign,即领域驱动设计。DDD不是一种架构,而是一种方法论(Methodology)。微服务架构自问世以来,并没有很好的理论支撑。如何合理定义服务边界。我们早期常见的软件开发是拿到产品需求后首先考虑数据库设计,根据数据库设计建立相应的实体层、服务层等,但这种方式会把分析、设计和业务脱节requirements,andmore大部分直接考虑如何实现,有点本末倒置,而DDD是一种从问题本身出发的设计方法。理念:系统设计应该是以领域为中心的设计和开发。设计应该通过维护一个深度反映领域概念的模型并提供大量可行和经过验证的模式来处理领域的复杂性。DDD更像是小粒子的迭代设计。最小的单元是领域模型(DomainModel)。什么是域?什么是域?有相应的核心环境,如商品浏览、购物车、下单、库存扣除、供应商、支付等。再比如我们要搭建一个聊天系统,那么系统的核心业务就必须确定,比如联系人、群组、朋友圈、视频、聊天记录等功能。因此,我们可以知道,一个领域的本质可以理解为一个问题域。只要属于同一个领域,就一定有同一个问题域。因此,只要我们确定了系统所属的领域,那么系统的核心业务就是我们要解决的重点问题,问题的范围和边界就基本确定了。什么是设计?DDD中的设计主要是指领域模型的设计。为什么是领域模型的设计而不是架构设计或者其他设计呢?因为DDD是一种基于模型驱动开发的软件开发思想,强调领域模型是这个系统的核心,领域模型也是整个系统的核心价值所在。每个领域都有对应的领域模型,因为领域模型可以很好的帮助我们解决复杂的业务问题。领域模型绑定领域,代码吸收,确保最终的代码实现必须解决领域中的核心问题。四色原型建模简述:一个人(Party)角色(PartyRole)在某个地方(Place)的角色(PlaceRole)使用某个事物(ThingRole)的角色(ThingRole)做某事(MomentInterval))PartPlaceThing:简称PPT,用浅绿色表示。人、地、物等Description:简称Des,用淡蓝色表示,主要用于描述PPT,常用Des包括:部门类型、职位级别、人员类型、所在区域、物品分类等。作用:表示淡黄色,主要表示PPT在某个场景中所起到的作用。常见角色包括:财务部、管理岗位、请假人、销售点、产品等。MomentInterval:简称MI,用淡红色表示,主要表示某一时刻或一段时间内发生的事件。常见的MI包括:部门调动、岗位调动、员工离职、产品销售等。MomentInteval:简称MIDetail,用浅红色表示,主要表示MI的明细。5.分层体系结构分层体系结构将软件模块按照水平切分划分为多个层次。最基本的分层架构是三层:表现层、领域层和数据持久层。DDD中的四层架构:性能层、应用层、领域层和基础层四层中的应用层是三层架构中领域层的进一步拆分。但不管怎么分层,业务逻辑始终在领域层。三层架构:-表现层:负责向用户展示信息,接收用户指令。它需要负责处理显示逻辑。例如,当用户通过我们的系统进行信用卡还款时,系统会返回三种状态:未申请、处理中、处理完成。表层需要根据这种状态向用户返回不同的页面,根据这三种不同的状态向用户展示不同的中文说明。-领域层:负责表达业务逻辑,是整个系统的核心层。比如信用卡还款服务。-持久层:提供数据查询和存储服务,包括根据状态查询信用卡。四层架构:表示层:同三层架构的表示层。应用层:定义软件要完成的任务,不包括业务逻辑,而是协调,比如接受用户请求的任务。负责非业务逻辑(批量删除、修改等)领域层:同三层架构的领域层。基础层:为各层提供通用的技术能力。为领域层提供数据和文件存储。分层架构最重要的是每一层都注意自己的职责。持久层只负责提供数据的查询、更新、存储等服务,与业务逻辑无关。因此,持久层提供了根据还款情况查询信用卡的服务。增加可重用性的好处,后续领域层提供查询服务,在显示已偿还的信用卡服务时可以重用持久层。分层架构的好处分层架构的目的是通过关注点分离降低系统的复杂度,同时满足单一职责、高内聚、低耦合,提高复用性,降低维护成本。职责单一:每一层只负责一个职责,职责边界清晰。比如持久层只负责数据的查询和存储,领域层只负责处理业务逻辑。高内聚:层次结构是将相同的职责放在同一层,所有业务逻辑在领域层内聚。这样做有什么好处?试想一下,如果业务逻辑分散在每一层,修改功能需要在每一层都修改,测试业务逻辑需要测试所有层的代码,这样就增加了整个软件的复杂度和测试难度。低耦合:依赖很简单,上层只能依赖下层,不存在循环依赖。可复用:某种能力可以复用于多个业务流程。例如,持久层提供了根据还款状态查询信用卡的服务,可以用来判断信用卡的申请,或者显示未还款的信用卡。易维护:面对变化容易修改。将所有对外接口放在对外接口层。对外依赖的接口一旦修改,只需要改动这一层的代码即可。以上不仅是分层的好处,还有分层的原理。分层时需要遵循以上原则。分层不当会违背分层架构的初衷。分层架构的缺点分层架构也有几个缺点,开发成本高:由于多层各司其职,添加功能需要在多层中添加代码,这势必会增加开发成本。但是,合理的能力抽象可以提高复用性,降低开发成本。性能略低:业务流程需要经过多层代码处理,会消耗性能。可扩展性低:由于上下层耦合,部分功能变更可能涉及多层修改。六、领域模型(domainmodel)领域模型是领域中概念类或现实世界中对象的可视化表示。也称为概念模型、业务对象模型、领域对象模型、分析对象模型。它侧重于分析问题领域本身,发现重要的业务领域概念,并建立业务领域概念之间的关系。优点是系统层次清晰,层与层之间单向依赖,Client->(BusinessFacade)->BusinessLogic->DataAccessObject可见,领域对象几乎只作为传输媒介,不会影响划分层。域对象仅用于保存状态或传输状态。它是没有生命的,没有行为的有数据的对象不是真实的对象。7.贫血模型贫血模型是指领域对象(一般指POJO)中只有get和set方法,所有的业务逻辑都不包含在其中,而是放在BusinessLogic层。在使用Spring时,通常意味着我们正在使用贫血模型。我们使用Domain类来简单地存储数据。Spring不关心这些类的注入和管理。优化的业务逻辑层)可以设计成一个SingletonBean。假设我们在这里进行了改动,在Domain类中提供了业务逻辑方法,那么我们在使用Spring构造这类数据bean的时候就会遇到很多麻烦,比如bean之间的引用,可能会导致Bean范围很广。转义构造函数调用。8、充血模型的大部分业务逻辑和持久化都放在DomainObject中。BusinessLogic只是简单封装了部分业务逻辑,控制了事务、权限等,这样的层次结构就变成了Client->(BusinessFacade)->BusinessLogic->DomainObject->DataAccessObject。优点:面向对象,业务逻辑符合单一职责,不会像贫血模型那样把所有的业务逻辑都包含进去不会太重。缺点业务逻辑怎么划分,什么样的逻辑应该放在DomainObject中,什么样的业务逻辑应该放在BusinessLogic中,其实都比较模糊。即使划分业务逻辑,因为分散在BusinessLogic和DomainObject层,模块开发也不能更好的划分。熟悉业务逻辑的开发者需要渗透DomainLogic,而DomainLogic包含持久化,这对开发者来说非常迷惑。BusinessLogic要想控制事务,为上层提供统一的服务调用入口,就必须将DomainLogic中实现的所有业务逻辑重新打包,完全是重复的。用RoR开发时,每个领域模型对象都可以有自己的基本业务方法,通常满足拥塞模型的开发,更适合复杂业务逻辑的设计和开发。充血模型的层级和模块划分是一门学问,对开发者的要求比较高。可以考虑这样定义一些规则:(1)事务控制不应该在接收模型的对象中实现,而可以在门面完成。(2)领域模型对象中只保留模型驱动的通用方法,在门面完成对具有明显业务特征的特定场景的方法调用。9.传统的数据驱动开发模型View,Service,Dao这种三层模型,开发者自然会写程序化的代码,这种开发方式中的对象只是一个数据载体,没有行为,是一种贫血的对象模型以数据为中心,由数据库ER图驱动。在这种开发模式下,分层架构可以看作是数据处理和实现的过程。10.BoundedContextBoundedContext:定义了每个模型的适用范围。一个业务域可以划分为多个BC,这些BC通过ContextMap进行整合。BC是一个明确的边界,领域模型Bianc存在于这个边界内。领域模型是关于特定业务领域的软件模型。通常,领域模型是通过对象模型来实现的。这些对象既包含数据,也包含行为,表达了准确的业务含义。关于boundedcontext,有一个很形象的分类,细胞和细胞膜的类比:细胞之所以能够存在,是因为细胞膜定义了细胞内和细胞外的东西,并确定了哪些物质可以通过细胞膜BC可以比作细胞膜等大系统,由于其复杂性,如果对一个物体采用统一的建模方法,可能会产生不可预知的效果。比如书中提到的客户发票中收费对象的失效,其实类似的问题在很多地方都可以看到。11.什么是CQRS?CQRS-CommandQueryResponsibilitySegreation(命令查询责任分离),顾名思义就是一种命令和查询分离的模式。命令和查询的这种分离允许更好地控制请求者的操作。查询操作不会引起数据修改,是幂等操作,可以反复发起,不用担心影响系统。基于这个特性,我们还可以为它提供缓存,以提高查询性能。query很容易理解,就是我们经常使用的query。那么什么是命令呢?我们可以看一下CRUD,它可以分为读(R)和写(CUD)。大多数情况下,一个方法要么是执行一个Command来完成一个动作,要么是查询并返回数据。例如,我们回答问题的人不应该修改问题。只要充分理解使用CQRS模式的意图,理解CQRS模式就会变得容易很多。下图是CQRS框架AxonFramework官方文档给出的CQRS架构图。12.UbiquitousLanguage(无处不在的语言)业务人员使用和我们一样的语言。例如,我们的程序允许业务尽可能集中在域中。比如在传统的数据驱动系统中,如果张三喜欢李四,我们一般会这样写UserService.Love(zhangsan,lisi)但我们业务人员很奇怪whoLoveswho?为什么是用户服务?如果我们这样写zhangsan.Love(lisi)如果我们用Company.hire(employee)代替companyservice.hire(company,employee)这样我们更容易涉及到业务人员,代码也更容易代表真实业务场景。以上是根据课堂笔记记录的。如果有什么错误或者不明白的地方,请在下方留言。
