0%

系统结构分层规范总结

一、通用DDD架构

首先,看下图:
系统分层结构对比

左图、是经典的DDD架构分层图。右图、在使用依赖倒置原则下的分层图。

两个结构图对比,最典型的区别就是基础设施层,在最上层还是最底层。由于,我们项目都在使用spring或者guice等ioc工具,所以推荐使用右图的分层架构。

在DDD中,推崇使用端口适配器模式(也叫六边形架构)来做服务代码边界切分,系统内部处理自己的领域服务,通过适配器接口方式和外部进行交互。简而言之,即模块层次之间面向接口开发,内部无约束。如下图:
六边形架构

二、项目结构分层实践

按照DDD架构的思想,结合我们日常使用场景,列出通用的项目代码模块。

1
2
3
4
5
6
7
8
9
10

<modules>
<module>dragonfly-api</module>
<module>dragonfly-infrastructure</module>
<module>dragonfly-deploy</module>
<module>dragonfly-application</module>
<module>dragonfly-presentation</module>
<module>dragonfly-domain</module>
<module>dragonfly-common</module>
</modules>

对外部署,为dragonfly-deploy模块,该模块会集成其他所有模块,因此,给出目前项目结构图:
DDD代码结构分层

** 和传统分层架构,最大的不同,在于我们把基础设施层dragonfly-infrastructure放在最上层。参考六边形架构,我们其实只区分上下两层,基础设施层位于上层,其他位于下层。**

** 因此,基础设施层负责实现图三中应用内部整块的适配器(接口),而领域层,应用层负责提供需要外部实现的能力接口即可。**

下面,具体来介绍每一个模块(排除dragonfly-api和dragonfly-deploy两个场景模块)的定位和使用。

2.1 基础设施层 dragonfly-infrastructure

1
2
3
4
5
6
7
8
9
10
/**
* 基础设施层。
* <p>
* 最上层的基础设施层,主要提供使用基础能力的实现,例如持久化能力,nsq、邮件、短信等异步消息能力,还有远程服务调用能力等。
* 位于最上层,需要依赖spring框架的依赖倒置能力,这样子使用方模块只需要调用接口,而不需要引入实现类。
* <p>
* 例如,仓储层的接口定义和实现,按照传统定义都在domain层。但是按照目前的定位,则仓储层的接口定义在domain层,而实现则在基础设施层。
* 至于,基础设施层,是使用mysql存储,还是使用hbase,es持久化,则是基础设施层实现接口的多个adapter后的设置。
* </p>
*/

2.2 用户接口层 dragonfly-presentation

1
2
3
4
5
6
7
8
9
10
11
/**
* 用户接口层(展现层)
* <p>
* 用户接口层,只处理用户显示和用户请求。它不包含领域或者业务逻辑。一些对用户参数的简单合法性验证,可以在该层进行;
* 但是如果涉及权限,复杂的判断等,则不归属该层进行。
* <p>
* 为了和领域模型进行解耦,在展现层会有自己的展现模型(Request(xxDto) & Result(xxDto))进行对外数据输出。
* <p>
* 用户接口层,是应用层的直接客户。
* </p>
*/

2.3 应用层 dragonfly-application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 应用层。
*
* <p>
* 应用服务,位于应用层中。应用服务和领域服务是不同的,因此领域服务不应该出现在应用服务中。
* <p>
* 应用服务,可以用于控制持久化事务和安全认证,或者向其他系统发送事件的消息通知,另外还可以用于创建邮件以发送给用户。
* 应用服务不处理业务逻辑,它是领域模型的直接客户,应用服务是轻量级的,主要用于协调对领域对象的操作,例如聚合。同时,
* 应用服务是表达用例和用户故事的主要手段。
* <p>
* 因此,应用服务的通常用途:接收来自用户界面(展现层)的输入参数,通过仓储服务获取聚合实例,然后执行相应的命令操作即可;
* 当然,应用服务,也可以拿到领域服务,完成相关领域的任务操作。
* </p>
*/

2.4 领域层 dragonfly-domain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 领域层。
* <p>
* 在DDD最核心的模块,就是领域层。在领域层中,主要包括实体(唯一id,生命周期),值对象(不会变化,直接替换,没有id来表示值对象的唯一性)。
* 此外,还有领域服务和领域事件。
* <p>
* 需要重点的说,在DDD中,实体内部是会有业务操作的,因此领域服务,是在实体内部不好实现的情况下,才出现的。比如操作多个领域对象来生成一个值对象,
* 或者对一个领域对象进行转换等。随意的领域服务,就会导致领域对象的贫血模型。
* <p>
* 实体具有哪些行为,决定了实体内部需要实现哪些服务。比如,一个账号,有状态转变,有效无效,绑定等,这些就是这个实体的行为,也就是实体内部要实现的方法。
* <p>
* 此外,如果一个服务没有多种实现,并且也不存在接口和实现位于多个模块中时,不需要去额外定义一个接口。直接创建一个实现类即可。
* </p>
*/

2.5 通用工具集 dragonfly-common

1
2
3
4
5
6
7
8
9
10
11
/**
* 通用工具集。
* <p>
* 这个模块,主要聚合一些通用的工具类,还有包括一些常量,自定义异常,错误码等,便于统一管理。
* <p>
* 对于一些特定的工具类,由于在使用的时候,可能做了入参的前置校验和场景确认,在工具方法实现的时候,最简化处理。这种工具类NOT!!!放在这里,
* 避免其他人直接使用,导致非预期结果。
* <p>
* 由于通用工具集,提供项目中其他所有模块使用,所以这里位于所有模块的最底层。
* </p>
*/

三、一些实践中的重点

待后续补充