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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

前言介绍

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

0.jpg

开发目标

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

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

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

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

服务架构

1.jpg

  • 应用层{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框架等,让整个架构更利于互联网开发。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
设计模式 缓存 自然语言处理
DDD领域驱动设计如何进行工程化落地
DDD领域驱动设计到底如何进行实际的工程化落地,为什么要进行领域分层?本文主要围绕DDD领域分层,设计了可落地的工程结构。
DDD领域驱动设计如何进行工程化落地
|
2月前
|
存储 前端开发 API
DDD领域驱动设计实战-分层架构
DDD分层架构通过明确各层职责及交互规则,有效降低了层间依赖。其基本原则是每层仅与下方层耦合,分为严格和松散两种形式。架构演进包括传统四层架构与改良版四层架构,后者采用依赖反转设计原则优化基础设施层位置。各层职责分明:用户接口层处理显示与请求;应用层负责服务编排与组合;领域层实现业务逻辑;基础层提供技术基础服务。通过合理设计聚合与依赖关系,DDD支持微服务架构灵活演进,提升系统适应性和可维护性。
|
4月前
|
缓存 架构师 中间件
成为工程师 - 如何做DDD领域驱动设计?
成为工程师 - 如何做DDD领域驱动设计?
|
消息中间件 开发者
DDD领域驱动设计实战(六)-领域服务(上)
DDD领域驱动设计实战(六)-领域服务
467 0
DDD领域驱动设计实战(六)-领域服务(上)
|
前端开发 架构师 Java
领域驱动设计DDD从入门到代码实践
在本文中,作者将借鉴《实现领域驱动设计》的做法,介绍领域驱动设计的基本概念的同时,用一个虚拟的公司和一个虚拟的项目,把领域驱动设计进行落地实践。
13474 9
领域驱动设计DDD从入门到代码实践
|
Java API 领域建模
领域驱动设计(DDD)-简单落地
一、序言     领域驱动设计是一种解决业务复杂性的设计思想,不是一种标准规则的解决方法。在本文中的实战示例可能会与常见的DDD规则方法不太一样,是简单、入门级别,新手可以快速实践版的DDD。如果不熟悉DDD设计思想可看下基础思想篇 二、设计阶段     领域建模设计阶段常见的方法有 四色建模法、EventSourcing等 推荐一篇博文正确理解领域建
12184 1
|
消息中间件 JavaScript 小程序
领域驱动设计(DDD)的几种典型架构介绍
领域驱动设计(DDD)的几种典型架构介绍
|
敏捷开发 架构师 领域建模
别吵,可落地的DDD!
别吵,可落地的DDD!
495 0
|
存储 消息中间件 JSON
领域驱动设计:从理论到实践,一文带你掌握DDD!
学习DDD一个半月,最开始学习DDD的原因是因为我负责的业务线,涉及的系统非常多,想借鉴领域驱动设计的思想,看后续如何对系统进行重构。在没有学习DDD之前,感觉DDD可能属于那种“虚头巴脑”的东西,学完DDD之后,感觉。。。嗯。。。真香!
1800 0
领域驱动设计:从理论到实践,一文带你掌握DDD!