图片

作者:付政委

纳百川、吞吐、成自卧龙,笑红尘、纷繁、当乃胸容

微信公众号:bugstack虫洞栈
领取驱动设计DDD{Domain-Driven Design}历史较长但随着微服务的兴起DDD又活跃到开发工程师的视线。它提供的是一套架构设计思想,我们可以使用这套方法论将架构设计的尽可能做到高内聚、低耦合、可扩展性强的应用服务。那么本专题以DDD实战落地为根本,分章节设计不同的架构模型。
学习并实战是奔入应用级开发最快的方法,Hi HelloWorld!我来了。

前言介绍

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

图片

微信公众号:bugstack虫洞栈 | DDD概述

开发目标

依靠领域驱动设计的设计思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合DDD分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照DDD设计思想完成微服务设计和开发。
1、拒绝泥球小单体、拒绝污染功能与服务、拒绝一加功能排期一个月
2、架构出高可用极易符合互联网高速迭代的应用服务
3、物料化、组装化、可编排的服务,提高人效

服务架构

图片

微信公众号:bugstack虫洞栈 | 服务架构

  • 应用层{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框架等,让整个架构更利于互联网开发。


图片

微信公众号:bugstack虫洞栈,欢迎关注&获取源码