别再搞三层架构了!这 3 种 DDD 分层架构的模式,不香吗?
- 前言 -
- DDD 的基本概念 -
微服务中应该首先建立UL,然后再讨论领域模型。 一个微服务最大不要超过一个BC,否则微服务内会存在有歧义的领域概念。 一个微服务最小不要小于一个聚合,否则会引入分布式事务的复杂度。 微服务的划分过程类似于BC的划分过程,每个微服务都有一个领域模型。 微服务间的集成可以通过Context Map来完成,比如ACL(Anticorruption Layer,防腐层)。 微服务间最好采用Domain Event(领域事件)来进行交互,使得微服务可以保持松耦合。 ……
- 分层架构 -
1. 开发人员可以只关注整个结构中的某一层。 2. 可以很容易的用新的实现来替换原有层次的实现。 3. 可以降低层与层之间的依赖。 4. 有利于标准化。 5. 利于各层逻辑的复用。
1. 降低了系统的性能。这是显然的,因为增加了中间层,不过可以通过缓存机制来改善。 2. 可能会导致级联的修改。这种修改尤其体现在自上而下的方向,不过可以通过依赖倒置来改善。
- 模式一:四层架构 -
1. User Interface为用户界面层(或表示层),负责向用户显示信息和解释用户命令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。 2. Application为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。 3. Domain为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层。 4. Infrastructure层为基础实施层,向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。传统的四层架构都是 限定型松散分层架构 ,即Infrastructure层的任意上层都可以访问该层(“L”型),而其它层遵守 严格分层架构
1. User Interface层主要是Restful消息处理,配置文件解析,等等。 2. Application层主要是多进程管理及调度,多线程管理及调度,多协程调度和状态机管理,等等。 3. Domain层主要是领域模型的实现,包括领域对象的确立,这些对象的生命周期管理及关系,领域服务的定义,领域事件的发布,等等。 4. Infrastructure层主要是业务平台,编程框架,第三方库的封装,基础算法,等等。
- 模式二:五层架构 -
传统上,面向对象编程语言拿不出办法去捕捉对象之间的协作,反映不了协作中往来的算法。就像对象的实例反映出领域结构一样,对象的协作与交互同样是有结构的。协作与交互也是最终用户心智模型的组成部分,但你在代码中找不到一个内聚的表现形式去代表它们。在本质上,角色体现的是一般化的、抽象的算法。角色没有血肉,并不能做实际的事情,归根结底工作还是落在对象的头上,而对象本身还担负着体现领域模型的责任。
1. Data层描述系统有哪些领域概念及其之间的关系,该层专注于领域对象的确立和这些对象的生命周期管理及关系,让程序员站在对象的角度思考系统,从而让“系统是什么”更容易被理解。 2. Context层:是尽可能薄的一层。Context往往被实现得无状态,只是找到合适的role,让role交互起来完成业务逻辑即可。但是简单并不代表不重要,显示化context层正是为人去理解软件业务流程提供切入点和主线。 3. Interactive层主要体现在对role的建模,role是每个context中复杂的业务逻辑的真正执行者,体现“系统做什么”。role所做的是对行为进行建模,它联接了context和领域对象。由于系统的行为是复杂且多变的,role使得系统将稳定的领域模型层和多变的系统行为层进行了分离,由role专注于对系统行为进行建模。该层往往关注于系统的可扩展性,更加贴近于软件工程实践,在面向对象中更多的是以类的视角进行思考设计。 DCI目前广泛被看作是对DDD的一种发展和补充,用在基于面向对象的领域建模上。显式的对role进行建模,解决了面向对象建模中的充血模型和贫血模型之争。DCI通过显式的用role对行为进行建模,同时让role在context中可以和对应的领域对象进行绑定(cast),从而既解决了数据边界和行为边界不一致的问题,也解决了领域对象中数据和行为高内聚低耦合的问题。
面向对象建模面临的一个棘手问题是数据边界和行为边界往往不一致。遵循模块化的思想,我们通过类将行为和其紧密耦合的数据封装在一起。但是在复杂的业务场景下,行为往往跨越多个领域对象,这样的行为如果放在某一个对象中必然会导致别的对象需要向该对象暴漏其内部状态。所以面向对象发展的后来,领域建模出现两种派别之争,一种倾向于将跨越多个领域对象的行为建模在领域服务中。如果这种做法使用过度,则会导致领域对象变成只提供一堆get方法的哑对象,这种建模结果被称之为贫血模型。而另一派则坚定的认为方法应该属于领域对象,所以所有的业务行为仍然被放在领域对象中,这样导致领域对象随着支持的业务场景变多而变成上帝类,而且类内部方法的抽象层次很难一致。另外由于行为边界很难恰当,导致对象之间数据访问关系也比较复杂,这种建模结果被称之为充血模型。
人有多重角色,不同的角色履行的职责不同。
1. 作为父母:我们要给孩子讲故事,陪他们玩游戏,哄它们睡觉。 2. 作为子女:我们要孝敬父母,听取他们的人生建议。 3. 作为下属:我们要服从上司的工作安排,并高质量完成任务。 4. 作为上司:我们要安排下属的工作,并进行培养和激励。 5. ……
这里人(大对象)聚合了多个角色(小类),人在某种场景下,只能扮演特定的角色:
1. 在孩子面前,我们是父母。 2. 在父母面前,我们是子女。 3. 在上司面前,我们是下属。 4. 在下属面前,我们是上司。 5. ……
1. Domain层只保留了DCI中的Data层和Interaction层,我们在实践中通常将这两层使用目录隔离,即通过两个目录object和role来分离层Data和Interaction。
DCI中的Context层从Domain层上移变成Context层。
因此,DDD分层架构模式就变成了五层,如下图所示:
1. User Interface是用户接口层,主要用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给Application层的接口。 2. Application层是应用层,负责多进程管理及调度、多线程管理及调度、多协程调度和维护业务实例的状态模型。当调度层收到用户接口层的请求后,委托Context层与本次业务相关的上下文进行处理。 3. Context是环境层,以上下文为单位,将Domain层的领域对象cast成合适的role,让role交互起来完成业务逻辑。 4. Domain层是领域层,定义领域模型,不仅包括领域对象及其之间关系的建模,还包括对象的角色role的显式建模。 5. Infrastructure层是基础实施层,为其他层提供通用的技术能力:业务平台,编程框架,持久化机制,消息机制,第三方库的封装,通用算法,等等。
1. Context层在面向控制面或管理面且消息交互比较多的系统中又分裂成两层,即Context层和大Context层。 2. Context层处理单位为Action,对应一条同步消息或异步消息。 3. 大Context层对应一个事务处理,由一个Action序列组成,一般通过Transaction DSL实现,所以我们习惯把大Context层叫做Transaction DSL层。 4. Application层在面向控制面或管理面且消息交互比较多的系统中经常会做一些调度相关的工作,所以我们习惯把Application层叫做Scheduler层。
1. User Interface是用户接口层,主要用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给Scheduler层的接口。 2. Scheduler是调度层,负责多进程管理及调度、多线程管理及调度、多协程调度和维护业务实例的状态模型。当调度层收到用户接口层的请求后,委托Transaction层与本次操作相关的事务进行处理。 3. Transaction是事务层,对应一个业务流程,比如UE Attach,将多个同步消息或异步消息的处理序列组合成一个事务,而且在大多场景下,都有选择结构。万一事务执行失败,则立即进行回滚。当事务层收到调度层的请求后,委托Context层的Action进行处理,常常还伴随使用Context层的Specification(谓词)进行Action的选择。 4. Context是环境层,以Action为单位,处理一条同步消息或异步消息,将Domain层的领域对象cast成合适的role,让role交互起来完成业务逻辑。环境层通常也包括Specification的实现,即通过Domain层的知识去完成一个条件判断。 5. Domain层是领域层,定义领域模型,不仅包括领域对象及其之间关系的建模,还包括对象的角色role的显式建模。 6. Infrastructure层是基础实施层,为其他层提供通用的技术能力:业务平台,编程框架,持久化机制,消息机制,第三方库的封装,通用算法,等等。
- 模式三:六边形架构 -
依赖倒置原则由Robert C. Martin提出,正式定义为:
高层模块不应该依赖于底层模块,两者都应该依赖于抽象。
抽象不应该依赖于细节,细节应该依赖于抽象。
Cockburn在2005年提出的,在这种架构中,不同的客户通过“平等”的方式与系统交互。需要新的客户吗?不是问题。只需要添加一个新的适配器将客户输入转化成能被系统API所理解的参数就行。同时,对于每种特定的输出,都有一个新建的适配器负责完成相应的转化功能。
端口并没有明确的定义,它是一个非常灵活的概念。无论采用哪种方式对端口进行划分,当客户请求到达时,都应该有相应的适配器对输入进行转化,然后端口将调用应用程序的某个操作或者向应用程序发送一个事件,控制权由此交给内部区域。应用程序通过公共API接收客户请求,使用领域模型来处理请求。
我们在实际的项目开发中,不同层的组件可以同时开发。当一个组件的功能明确后,就可以立即启动开发。由于该组件的用户有多个,并且这些用户的侧重点不同,所以需要提供多个不同的接口。同时,这些用户的认识也是不断深入的,可能会多次重构相关的接口。于是,组件的多个用户经常会找组件的开发者讨论这些问题,无形中降低了组件的开发效率。
- 六边形架构模式的演变 -
1. Jeffrey Palermo在2008年提出了 洋葱架构 ,六边形架构是洋葱架构的一个超集。 2. Robert C. Martin在2012年提出了干净架构(Clean Architecture),这是六边形架构的一个变体。 3. Russ Miles在2013年提出了 Life Preserver 设计,这是一种基于六边形架构的设计。
- 总结 -
来源:
https://www.jianshu.com/p/a775836c7e25
评论