领域驱动设计核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。这几年之所以开始火起来,主要功劳要归功于队友“微服务”,领域驱动设计与微服务架构天生匹配。
领域驱动设计(DDD)是一种处理高度复杂域的设计思想,试图分离技术实现的复杂性,围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演化等问题。团队利用它可以成功的开发复杂业务软件系统,在系统变大时仍能保持敏捷性。
领域驱动设计分为两个阶段:
领域驱动设计的核心诉求是让业务架构和系统架构形成绑定关系,当我们去响应业务变化调整业务架构时,系统架构的改变也会随之发生。在领域驱动设计中业务架构的梳理和系统架构的梳理是同步进行的,其结果是设计出的业务上下文和系统模块结构是绑定的。同时技术架构也是解耦的,可以根据划分出来的业务上下文的系统架构选择最合适的实现技术。
领域驱动设计可能会给你带来以下收获:
1、领域驱动设计是一套完整而系统的设计方法,它能带给你从战略设计到战术设计的规范过程,使得你的设计思路能够更加清晰,设计过程更加规范。
3、领域驱动设计强调团队与领域专家的合作,能够帮助团队建立一个沟通良好的团队组织,构建一致的架构体系。领域驱动设计强调对架构与模型的精心打磨,尤其善于处理系统架构的演进设计。
4、领域驱动设计的思想、原则与模式有助于提高团队成员的架构设计能力。
5、领域驱动设计与微服务架构天生匹配,无论是在新项目中设计微服务架构,还是将系统从单体架构演进到微服务设计,都可以遵循领域驱动设计的架构原则。
在研究和解决业务问题时,DDD会按照一定的规则将业务领域进行细分,当领域细分到一定的程度后,DDD会将问题范围限定在特定的边界内,在这个边界内建立领域模型,进而用代码实现该领域模型,解决相应的业务问题。简言之,DDD的领域就是这个边界内要解决的业务问题域。
领域可以进一步划分为子领域。我们把划分出来的多个子领域称为子域,每个子域对应一个更小的问题域或更小的业务范围。
领域的核心思想就是将问题域逐级细分,来降低业务理解和系统实现的复杂度。通过领域细分,逐步缩小服务需要解决的问题域,构建合适的领域模型。
举个简单的例子,对于保险领域,我们可以把保险细分为承保、收付、再保以及理赔等子域,而承保子域还可以继续细分为投保、保全(寿险)、批改(财险)等子子域。
通用语言也有它的上下文环境,为了避免同样的概念或语义在不同的上下文环境中产生歧义,DDD在战略设计上提出了“限界上下文”这个概念,用来确定语义所在的领域边界。
限界上下文是一个显式的语义和语境上的边界,领域模型便存在于边界之内。边界内,通用语言中的所有术语和词组都有特定的含义。把限界上下文拆解开看,限界就是领域的边界,而上下文则是语义环境。
通过领域的限界上下文,我们就可以在统一的领域边界内用统一的语言进行交流
聚合:我们把一些关联性极强、生命周期一致的实体、值对象放到一个聚合里。聚合是领域对象的显式分组,旨在支持领域模型的行为和不变性,同时充当一致性和事务性边界。
聚合在DDD分层架构里属于领域层,领域层包含了多个聚合,共同实现核心业务逻辑。跨多个实体的业务逻辑通过领域服务来实现,跨多个聚合的业务逻辑通过应用服务来实现。
如果把聚合比作组织,那聚合根就是这个组织的负责人。聚合根也称为根实体,它不仅是实体,还是聚合的管理者。首先它作为实体本身,拥有实体的属性和业务行为,实现自身的业务逻辑。其次它作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。
当一些逻辑不属于某个实体时,可以把这些逻辑单独拿出来放到领域服务中,理想的情况是没有领域服务,如果领域服务使用不恰当,慢慢又演化回了以前逻辑都在service层的局面。
可以使用领域服务的情况:
应用层作为展现层与领域层的桥梁,是用来表达用例和用户故事的主要手段。
应用层通过应用服务接口来暴露系统的全部功能。在应用服务的实现中,它负责编排和转发,它将要实现的功能委托给一个或多个领域对象来实现,它本身只负责处理业务用例的执行顺序以及结果的拼装。通过这样一种方式,它隐藏了领域层的复杂性及其内部实现机制
应用层相对来说是较“薄”的一层,除了定义应用服务之外,在该层我们可以进行安全认证,权限校验,持久化事务控制,或者向其他系统发生基于事件的消息通知,另外还可以用于创建邮件以发送给客户等
领域事件=事件发布+事件存储+事件分发+事件处理。
下面简单说明领域事件:
比如下订单后,给用户增长积分与赠送优惠券的需求。如果使用瀑布流的方式写代码。一个个逻辑调用,那么不同用户,赠送的东西不同,逻辑就会变得又臭又长。
这里的比较好的方式是,用户下订单成功后,发布领域事件,积分聚合与优惠券聚合监听订单发布的领域事件进行处理
二、领域驱动设计三大核心思想
1、领域模型驱动架构
限定上下文,我理解下来就是能够实施统一语言的一个范围。所谓上下文,就是保持理解的一致。比如利润,在财务系统中利润包含成本、收入、计算方法等,是个很重要的Entity。而在商品系统中,它只是商品的一个属性,商品只需要利润的一个总值而已。这里我们所说利润在财务与商品两个上下文中有不同的含义。再说一下限定上下文与微服务。限定上下文是对于领域的划分,在一个限定上下文大家对模型的理解一致就行。而微服务更多是对应用的拆分。多个微服务应用,可以在一个限定上下文里面,比如财务域限定上下文,里面可以有结算服务、计费服务等。再者就是模块与限定上下文的区分,限定上下文就不说了,模块更多的时指一下命令空间,不管是应用代码中的模块,还是业务上的模块,都是给代码或者业务加上一个空间范围。
限界上下文划分规则
一般来说,先考虑团队规模,来决定最终需要划分到多细粒度的BC,如果团队规模过小而BC过细,则对后期的运维、部署、上线都会造成很大的负担;
按照以上的规则划分之后就得到了多个BC啦
领域驱动设计的四重边界
根据上图所示,我们通过四重来进行架构设计:
【第一重边界】确定项目的愿景与目标,确定问题空间,确定核心子领域、通用子领域(多个子领域可以复用)、支撑子领域(额外功能,如数据统计、导出报表)
【第二重边界】解决方案空间里的限界上下文就是一道进程隔离层面的物理边界
【第三重边界】每个限界上下文内,使用分层架构划分为:接口层、领域层、应用层、基础设施层之间的最小隔离
【第四重边界】领域层里为了保证各个领域的完整性和一致性,引入聚合的设计作为隔离领域模型的最小单元
2、SOA和横向分层
为什么领域驱动设计是微服务架构的最佳设计方法?
领域驱动设计作为一种架构设计方法,微服务作为一种架构风格,两者从本质上都是为追求高响应力目标而从业务视角去分离复杂度的手段。两者都强调从业务出发,其核心要义强调根据业务发展,合理划分领域边界,持续调整现有架构,优化现有代码,以保持架构和代码的生命力(演进式架构)。
如果你的业务焦点在领域和领域逻辑,那么你就可以选择DDD进行微服务架构设计。
严格分层架构:某层只能与直接位于的下层发生耦合
松散分层架构:允许上层与任意下层发生耦合
在领域驱动设计(DDD)中采用的是松散分层架构,层间关系不那么严格。每层都可能使用它下面所有层的服务,而不仅仅是下一层的服务。每层都可能是半透明的,这意味着有些服务只对上一层可见,而有些服务对上面的所有层都可见。
主动适配:指来于UI、命令等输型命令,controller就是种端,端的具体实现就是应逻辑身。因此端和具体实现都在应系统的内部。
被动适配:指访问存储设备,外部服务等。每种访问就是种端,具体实现是各个具体的中间件。因此端在整个应系统的部,具体实现在系统的外部。
每种输和输出都是个端,每个端都有具体的实现逻辑,因此整个应系统的架构就是些列的端+适配逻辑组成,架构图就是个多边形形状。有个端需要根据应系统的具体情况定,只是六个端较形象得名为六边形架构。
特点:
1.外层依赖内层使得依赖更合理。端就是接,依赖接编程。借此保证了应和实现细节之间的隔离。
2.可测试更好
3、EDA事件驱动架构
事件驱动架构
光从作法来看,EDA与传统架构的区别主要在于将传统架构处理数据库变更或者DDD中处理模型的变更转化成一个个标准的事件,至于事件的消费、事件的追溯则有具体的服务完成。
在《实现领域驱动设计》一书中提出采用EDA结合六边形架构的思路。
一个系统的输出端口所发出的领域事件将被发送到另一个系统的输人端口,此后输人端口的事件订阅方将对事件进行处理。对于不同的BC来说,不同的领域事件具有不同含义。在一个BC处理某个事件时,应用程序API将采用该事件中的属性值来执行相应的操作。
在一个多任务处理过程中,如果这个事务只有在所有的参与事件都得到处理之后,我们才能认为这个多任务处理过程完成了,某种领域事件只能表示该过程中的一部分。这时候这个过程的处理就需要结合其他架构进行处理。如果处理事件和消息在下面章节做介绍。