DDD专题案例一《初识领域驱动设计DDD落地方案》

前言介绍

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

DDD专题案例一《初识领域驱动设计DDD落地方案》

开发目标

依靠领域驱动设计的设计思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合DDD分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照DDD设计思想完成微服务设计和开发。

1、拒绝泥球小单体、拒绝污染功能与服务、拒绝一加功能排期一个月

2、架构出高可用极易符合互联网高速迭代的应用服务

3、物料化、组装化、可编排的服务,提高人效

服务架构

DDD专题案例一《初识领域驱动设计DDD落地方案》

  • 应用层{application}
    • 应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。
    • 应用层的服务包括应用服务和领域事件相关服务。
    • 应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。
    • 领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。
  • 领域层{domain}
    • 领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。
    • 领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。
    • 为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。
    • 为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。
  • 基础层{infrastructrue}
    • 基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。
    • 基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。
  • 接口层{interfaces}
    • 接口服务位于用户接口层,用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给应用层。

开发环境

1、jdk1.8【jdk1.7以下只能部分支持netty】

2、springboot 2.0.6.RELEASE

3、idea + maven

代码示例

1itstack-demo-ddd-01
 2└── src
 3    ├── main
 4    │   ├── java
 5    │   │   └── org.itstack.demo
 6    │   │       ├── application
 7    │   │       │    ├── event
 8    │   │       │    │   └── ApplicationRunner.java  
 9    │   │       │    └── service
10    │   │       │        └── UserService.java    
11    │   │       ├── domain
12    │   │       │    ├── model
13    │   │       │    │   ├── aggregates
14    │   │       │    │   │   └── UserRichInfo.java   
15    │   │       │    │   └── vo
16    │   │       │    │       ├── UserInfo.java   
17    │   │       │    │       └── UserSchool.java 
18    │   │       │    ├── repository
19    │   │       │    │   └── IuserRepository.java    
20    │   │       │    └── service
21    │   │       │        └── UserServiceImpl.java    
22    │   │       ├── infrastructure
23    │   │       │    ├── dao
24    │   │       │    │   ├── impl
25    │   │       │    │   │   └── UserDaoImpl.java    
26    │   │       │    │   └── UserDao.java    
27    │   │       │    ├── po
28    │   │       │    │   └── UserEntity.java 
29    │   │       │    ├── repository
30    │   │       │    │   ├── mysql
31    │   │       │    │   │   └── UserMysqlRepository.java
32    │   │       │    │   ├── redis
33    │   │       │    │   │   └── UserRedisRepository.java        
34    │   │       │    │   └── UserRepository.java 
35    │   │       │    └── util
36    │   │       │        └── RdisUtil.java
37    │   │       ├── interfaces
38    │   │       │    ├── dto
39    │   │       │    │   └── UserInfoDto.java    
40    │   │       │    └── facade
41    │   │       │        └── DDDController.java
42    │   │       └── DDDApplication.java
43    │   ├── resources    
44    │   │   └── application.yml
45    │   └── webapp    
46    │       └── WEB-INF
47    │            └── index.jsp   
48    └── test
49         └── java
50             └── org.itstack.demo.test
51                 └── ApiTest.java

演示部分重点代码块,完整代码下载关注公众号;bugstack虫洞栈 | 回复DDD落地

application/UserService.java | 应用层用户服务,领域层服务做具体实现

1/**
 2 * 应用层用户服务
 3 * 虫洞栈:https://bugstack.cn
 4 * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
 5 * Create by fuzhengwei on @2019
 6 */
 7public interface UserService {
 8
 9    UserRichInfo queryUserInfoById(Long id);
10
11}

domain/repository/IuserRepository.java | 领域层资源库,由基础层实现

1/**
 2 * 虫洞栈:https://bugstack.cn
 3 * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
 4 * Create by fuzhengwei on @2019
 5 */
 6public interface IUserRepository {
 7
 8    void save(UserEntity userEntity);
 9
10    UserEntity query(Long id);
11
12}

domain/service/UserServiceImpl.java | 应用层实现类,应用层是很薄的一层可以只做服务编排

1/**
 2 * 虫洞栈:https://bugstack.cn
 3 * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
 4 * Create by fuzhengwei on @2019
 5 */
 6@Service("userService")
 7public class UserServiceImpl implements UserService {
 8
 9    @Resource(name = "userRepository")
10    private IUserRepository userRepository;
11
12    @Override
13    public UserRichInfo queryUserInfoById(Long id) {
14
15        // 查询资源库
16        UserEntity userEntity = userRepository.query(id);
17
18        UserInfo userInfo = new UserInfo();
19        userInfo.setName(userEntity.getName());
20
21        // TODO 查询学校信息,外部接口
22        UserSchool userSchool_01 = new UserSchool();
23        userSchool_01.setSchoolName("振华高级实验中学");
24
25        UserSchool userSchool_02 = new UserSchool();
26        userSchool_02.setSchoolName("东北电力大学");
27
28        List<UserSchool> userSchoolList = new ArrayList<>();
29        userSchoolList.add(userSchool_01);
30        userSchoolList.add(userSchool_02);
31
32        UserRichInfo userRichInfo = new UserRichInfo();
33        userRichInfo.setUserInfo(userInfo);
34        userRichInfo.setUserSchoolList(userSchoolList);
35
36        return userRichInfo;
37    }
38
39}

infrastructure/po/UserEntity.java | 数据库对象类

1/**
 2 * 数据库实体对象;用户实体
 3 * 虫洞栈:https://bugstack.cn
 4 * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
 5 * Create by fuzhengwei on @2019
 6 */
 7public class UserEntity {
 8
 9    private Long id;
10    private String name;
11
12    get/set ...
13}

infrastructrue/repository/UserRepository.java | 领域层定义接口,基础层资源库实现

1/**
 2 * 虫洞栈:https://bugstack.cn
 3 * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
 4 * Create by fuzhengwei on @2019
 5 */
 6@Repository("userRepository")
 7public class UserRepository implements IUserRepository {
 8
 9    @Resource(name = "userMysqlRepository")
10    private IUserRepository userMysqlRepository;
11
12    @Resource(name = "userRedisRepository")
13    private IUserRepository userRedisRepository;
14
15    @Override
16    public void save(UserEntity userEntity) {
17        //保存到DB
18        userMysqlRepository.save(userEntity);
19
20        //保存到Redis
21        userRedisRepository.save(userEntity);
22    }
23
24    @Override
25    public UserEntity query(Long id) {
26
27        UserEntity userEntityRedis = userRedisRepository.query(id);
28        if (null != userEntityRedis) return userEntityRedis;
29
30        UserEntity userEntityMysql = userMysqlRepository.query(id);
31        if (null != userEntityMysql){
32            //保存到Redis
33            userRedisRepository.save(userEntityMysql);
34            return userEntityMysql;
35        }
36
37        // 查询为NULL
38        return null;
39    }
40
41}

interfaces/dto/UserInfoDto.java | DTO对象类,隔离数据库类

1/**
 2 * 虫洞栈:https://bugstack.cn
 3 * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
 4 * Create by fuzhengwei on @2019
 5 */
 6public class UserInfoDto {
 7
 8    private Long id;        // ID
 9
10    public Long getId() {
11        return id;
12    }
13
14    public void setId(Long id) {
15        this.id = id;
16    }
17
18}

interfaces/facade/DDDController.java | 门面接口

1/**
 2 * 虫洞栈:https://bugstack.cn
 3 * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
 4 * Create by fuzhengwei on @2019
 5 */
 6@Controller
 7public class DDDController {
 8
 9    @Resource(name = "userService")
10    private UserService userService;
11
12    @RequestMapping("/index")
13    public String index(Model model) {
14        return "index";
15    }
16
17    @RequestMapping("/api/user/queryUserInfo")
18    @ResponseBody
19    public ResponseEntity queryUserInfo(@RequestBody UserInfoDto request) {
20        return new ResponseEntity<>(userService.queryUserInfoById(request.getId()), HttpStatus.OK);
21    }
22
23}

综上总结

  • 以上基于DDD一个基本入门的结构演示完成,实际开发可以按照此模式进行调整。
  • 目前这个架构分层还不能很好的进行分离,以及层级关系的引用还不利于扩展。
  • 后续会持续完善以及可以组合搭建RPC框架等,让整个架构更利于互联网开发。
上一篇:netty案例,netty4.1中级拓展篇六《SpringBoot+Netty+Elasticsearch收集日志信息数据存储》


下一篇:netty案例,netty4.1中级拓展篇四《Netty传输文件、分片发送、断点续传》