在J2EE的开发模式中,Action(Controller)/Service/Model/DAO这种分层模式比较常见。分层架构在这种开发模式下,可以理解为是对数据移动、处理和实现的过程。但是你会发现以前学习的OOM根本用不上,所有的model都只有数据定义,不包含任何对象方法,有个专有名词叫“贫血症”。
当系统中业务越来越庞大后,会发现简单的分层模式已无法负荷繁重的业务,代码逻辑越来越不清楚,这时候有一种架构模式叫领域驱动设计(叫DDD,反正很讨厌一大堆名词,动不动给码农增加词汇)
架构定义
参考:https://www.jianshu.com/p/a775836c7e25?from=groupmessage
1.四层模式
- User Interface为用户界面层(或表示层),负责向用户显示信息和解释用户命令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。
- Application为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。
- Domain为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层。
- Infrastructure层为基础实施层,向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。
2.五层模式
- User Interface是用户接口层,主要用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给Application层的接口。
- Application层是应用层,负责多进程管理及调度、多线程管理及调度、多协程调度和维护业务实例的状态模型。当调度层收到用户接口层的请求后,委托Context层与本次业务相关的上下文进行处理。
- Context是环境层,以上下文为单位,将Domain层的领域对象cast成合适的role,让role交互起来完成业务逻辑。
- Domain层是领域层,定义领域模型,不仅包括领域对象及其之间关系的建模,还包括对象的角色role的显式建模。
- Infrastructure层是基础实施层,为其他层提供通用的技术能力:业务平台,编程框架,持久化机制,消息机制,第三方库的封装,通用算法,等等。
3.六层模式
- User Interface是用户接口层,主要用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给Scheduler层的接口。
- Scheduler是调度层,负责多进程管理及调度、多线程管理及调度、多协程调度和维护业务实例的状态模型。当调度层收到用户接口层的请求后,委托Transaction层与本次操作相关的事务进行处理。
- Transaction是事务层,对应一个业务流程,比如UE Attach,将多个同步消息或异步消息的处理序列组合成一个事务,而且在大多场景下,都有选择结构。万一事务执行失败,则立即进行回滚。当事务层收到调度层的请求后,委托Context层的Action进行处理,常常还伴随使用Context层的Specification(谓词)进行Action的选择。
- Context是环境层,以Action为单位,处理一条同步消息或异步消息,将Domain层的领域对象cast成合适的role,让role交互起来完成业务逻辑。环境层通常也包括Specification的实现,即通过Domain层的知识去完成一个条件判断。
- Domain层是领域层,定义领域模型,不仅包括领域对象及其之间关系的建模,还包括对象的角色role的显式建模。
- Infrastructure层是基础实施层,为其他层提供通用的技术能力:业务平台,编程框架,持久化机制,消息机制,第三方库的封装,通用算法,等等。
代码结构
参考:https://www.cnblogs.com/daoqidelv/p/7499662.html
User Interface层
门面层,对外以各种协议提供服务,该层需要明确定义支持的服务协议、契约等。包含:
dto
包括request和response两部分,通过它定义入参和出参的契约,在dto层可以使用基础设施层的validation组件完成入参格式校验;
controller
支持不同访问协议的控制器实现,比如:http/restful风格、tcp/二进制流协议、mq消息/json对象等等。
controller使用基础设施层公共组件完成许多通用的工作:
- 调用RequestMapping(SpringMVC公共组件)完成servlet路由;
- 调用checklogin完成登录态/权限校验;
- 调用logging组件完成日志记录;
- 调用message-resource组件完成错误信息转义,支持I18N;
application层
service
应用服务层,组合domain层的领域对象和基础设施层的公共组件,根据业务需要包装出多变的服务,以适应多变的业务服务需求。
应用服务层主要访问domain领域对象,完成服务逻辑的包装。
应用服务层也会访问基础设施层的公共组件,如rabbitmq,完成领域消息的生产等。
assembler
组装器,负责将多个domain领域对象组装为需要的dto对象,比如查询帖子列表,需要从Post(帖子)领域对象中获取帖子的详情,还需要从User(用户)领域对象中获取用户的基本信息。
组装器中不应当有业务逻辑在里面,主要负责格式转换、字段映射等职责。
domain层
业务领域层,是我们最应当关心的一层,也是最多变的一层,需要保证这一层是高内聚的。确保所有的业务逻辑都留在这一层,而不会遗漏到其他层。按照ddd(domain driven design)理论,主要有如下概念构成:
domain entity
领域实体。有唯一标识,可变的业务实体对象,它有着自己的生命周期。比如社区这一业务领域中,‘帖子’就是一个业务实体,它需要有一个唯一性业务标识表征,同时他的状态和内容可以不断发生变化。
domain value object
领域值对象。可以没有唯一性业务标识,且一旦定义,他是不可变的,它通常是短暂的。这和java中的值对象(基本类型和String类型)类似。比如社区业务领域中,‘帖子的置顶信息’可以理解为是一个值对象,不需要为这一值对象定义独立的业务唯一性标识,直接使用‘帖子id‘便可表征,同时,它只有’置顶状态‘和’置顶位置‘,一旦其中一个属性需要发生变化,则重建值对象并赋值给’帖子‘实体的引用,不会对领域带来任何负面影响。
domain factory
领域对象工厂。用于复杂领域对象的创建/重建。重建是指通过respostory加载持久化对象后,重建领域对象。
domain service
领域服务。区别于应用服务,他属于业务领域层。
可以认为,如果某种行为无法归类给任何实体/值对象,则就为这些行为建立相应的领域服务即可。比如:转账服务(transferService),需要操作借方/贷方两个账户实体。
传统意义上的util static方法中,涉及到业务逻辑的部分,都可以考虑归入domain service。
domain event
领域事件。领域中产生的一些消息事件,通过事件通知/订阅的方式,可以在性能和解耦层面得到好处。
repository
仓库。我们将仓库的接口定义归类在domain层,因为他和domain entity联系紧密。仓库用户和基础实施的持久化层交互,完成领域对应的增删改查操作。
仓库的实际实现根据不同的存储介质而不同,可以是redis、oracle、mongodb等。
鉴于现在社区服务的存储介质有三套:oracle、redis、mongodb,且各个存储介质的字段属性名不一致,因此需要使用translator来做翻译,将持久化层的对象翻译为统一的领域对象。
translator
翻译器。将持久化层的对象翻译为统一的领域对象。
翻译器中不应当有业务逻辑在里面,主要负责格式转换、字段映射等职责。
infrastructure层
基础设施层提供公共功能组件,供controller、service、domain层调用。
repository impl
对domain层repository接口的实现,对应每种存储介质有其特定实现,如oracle的mapper,mongodb的dao等等。repository impl会调用mybatis、mongo client、redis client完成实际的存储层操作。
checkLogin
权限校验器,判定客户端是否有访问该资源的权限。提供给User Interface层的Controller调用。
exception
异常分类及定义,同时提供公共的异常处理逻辑,具体由ExceptionHandler实现。
transport
transport完成和第三方服务的交互,可以有多种协议形式的实现,如http+json、tcp+自定义协议等,配套使用的还有Resolver解析器,用于对第三方服务的请求和响应进行适配,提供一个防腐层(AnticorruptionLayer,DDD原书P255)的作用。
transcation
提供事务管理,交给Spring管理。
logging
日志模块,记录trace日志,使用log4j完成。
message resource
消息资源管理,交给Spring统一管理。
实践关键
参考:https://blog.csdn.net/zhuiqiuuuu/article/details/87928349