DDD-分层设计
系统架构
包结构划分
user-api-用户接口层(适配器层)
- user-api 用户接口层: 收口应用所有的入口来源,应用的北向网关
- 也可以称作adapter适配器层: 对每种类型的客户都有它自己的适配器,该适配器用于将客户的请求转化为程序内部API所能理解的输入"。
-
- 外部消息处理
- 提供的hsf服务
- 事件处理
- 提供的web请求controller
理解:
- 在以前不同终端差异很大的时候,"每种客户都有它自己的适配器"可以认为有移动app、web和pad等不同终端需要适配;现在微服务盛行,在阿里系内就用HSF就完事了,HSF向上对接MTOP或者TOP就能完成不同端的适配,所以现在我们认为HSF就是一种客户或者适配器,放在Adaptor
- 此外系统内部接收外部系统的消息也可以认为是一种端口,所以消息消费者Consumer需要放在Adaptor
- 同时,现在很多系统仍然保留HTTP的Controller,用于对外网客户或者内部的管理后台提供服务,所以HTTP也可以认为是一种端口,Controller要放在Adaptor。
他们的职责都是负责将外部的输入参数(Request)转化为Application的服务入参(ApplicationRequest),将Application的服务出参(Response)转化为外部的输出(DTO)。除此以外,不做其他的工作。
application-应用服务层
只负责业务流程的串联,对不同领域的服务调用进行编排。
事务控制一般在Application层, 在该层我们可以进行安全认证,权限校验,持久化事务控制,或者向其他系统发生基于事件的消息通知,也是作为领域层和用户接口层的桥梁。
命名规范:command入参为XxxCmdRequest,query入参为XxxQueryRequest
例如:下图是付汇系统付汇单AppService创建付汇单的方法实现。编排了付汇单域、计税域、审批域的领域服务
ApplicationService的接口入参只能是一个Command、Query或Event对象,CQE对象需要能代表当前方法的业务语意。 ApplicationService集中罗列系统核心的业务用例(Command)。读写(Command和Query)建议分开,放到不同的Service。
infrastructure-基础设施层
核心职能是为各层的技术能力提供支持。
要注意,基础设施层的接口应该定义在domain(依赖倒置原则)隔离变与不变就是通过依赖倒置进行处理。
- 对于领域依赖的外部接口(integration)。通过"依赖倒置"在Domain层定义,在基础设施层实现。
- 对于领域对象的资源库(持久化)。通过在Domain层声明Repository,在基础设施层实现。
- 对于一些配置项(例如Diamond),也在基础设施层实现。
- 对于缓存、消息队列的实现也在基础设施层实现
domain-领域服务层
所有业务逻辑内聚在这一层
Domain层包含实体(Entity)、值对象(Value-Object)、事件、资源库声明、外部依赖声明和领域服务等组件。
- 实体(Entity)。有唯一性定义的领域对象就是实体,没有唯一性定义的领域对象就是值对象,业务逻辑的实现都围绕领域对象展开的。
- 值对象(Value-Object)。没有唯一性定义的领域对象就是值对象。值对象是很容易被我们忽略的组件,它本应该发挥更大的作用,关于值对象的运用大家可以参考Domain-Primitive这一篇文章,里面提出运用值对象的三原则让我获益匪浅:
- 领域事件(Event)。 当领域内发生了某件事,要通知其他域或者外部系统,就可以用领域事件;在Domain层定义的领域事件既可以是用于本次请求的线程内的事件,也可以是远程的metaq消息用于外部系统。
- 资源库声明(Repository-Declare) 。用于持久层增删改查的接口声明。
- 外部依赖声明(DI-Declare) 。Domain层的外部依赖一般进行接口声明,具体实现放在基础设施层。
- 领域服务(Domain-Service)。领域服务的作用跟通常的基于SpringMVC开发的service层是不一样,领域服务的定义和使用场景比较严肃,殷浩老师总结了领域服务使用的三种场景,目前我们一直按照这个来实践的:
dal-数据访问层
负责DB层的操作,只能由repository调用
- 定义及实现DB层的操作接口,供repository层调用