作者:小傅哥

博客:https://bugstack.cn

沉淀、分享、成长,让自己和他人都能有所收获!

一、前言

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

二、开发目标

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

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

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

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

三、服务架构

  • 应用层{application}

    • 应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。
    • 应用层的服务包括应用服务和领域事件相关服务。
    • 应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。
    • 领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。
  • 领域层{domain}

    • 领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。
    • 领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。
    • 为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。
    • 为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。
  • 基础层{infrastructrue}

    • 基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。
    • 基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。
  • 接口层{interfaces}

    • 接口服务位于用户接口层,用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给应用层。

四、开发环境

  1. jdk1.8【jdk1.7以下只能部分支持netty】
  2. springboot 2.0.6.RELEASE
  3. idea + maven

五、代码示例

itstack-demo-ddd-01
└── src
├── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── application
│ │ │ ├── event
│ │ │ │ └── ApplicationRunner.java
│ │ │ └── service
│ │ │ └── UserService.java
│ │ ├── domain
│ │ │ ├── model
│ │ │ │ ├── aggregates
│ │ │ │ │ └── UserRichInfo.java
│ │ │ │ └── vo
│ │ │ │ ├── UserInfo.java
│ │ │ │ └── UserSchool.java
│ │ │ ├── repository
│ │ │ │ └── IuserRepository.java
│ │ │ └── service
│ │ │ └── UserServiceImpl.java
│ │ ├── infrastructure
│ │ │ ├── dao
│ │ │ │ ├── impl
│ │ │ │ │ └── UserDaoImpl.java
│ │ │ │ └── UserDao.java
│ │ │ ├── po
│ │ │ │ └── UserEntity.java
│ │ │ ├── repository
│ │ │ │ ├── mysql
│ │ │ │ │ └── UserMysqlRepository.java
│ │ │ │ ├── redis
│ │ │ │ │ └── UserRedisRepository.java
│ │ │ │ └── UserRepository.java
│ │ │ └── util
│ │ │ └── RdisUtil.java
│ │ ├── interfaces
│ │ │ ├── dto
│ │ │ │ └── UserInfoDto.java
│ │ │ └── facade
│ │ │ └── DDDController.java
│ │ └── DDDApplication.java
│ ├── resources
│ │ └── application.yml
│ └── webapp
│ └── WEB-INF
│ └── index.jsp
└── test
└── java
└── org.itstack.demo.test
└── ApiTest.java

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

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

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

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

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

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

/**
* 虫洞栈:https://bugstack.cn
* 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
* Create by fuzhengwei on @2019
*/
@Service("userService")
public class UserServiceImpl implements UserService { @Resource(name = "userRepository")
private IUserRepository userRepository; @Override
public UserRichInfo queryUserInfoById(Long id) { // 查询资源库
UserEntity userEntity = userRepository.query(id); UserInfo userInfo = new UserInfo();
userInfo.setName(userEntity.getName()); // TODO 查询学校信息,外部接口
UserSchool userSchool_01 = new UserSchool();
userSchool_01.setSchoolName("振华高级实验中学"); UserSchool userSchool_02 = new UserSchool();
userSchool_02.setSchoolName("东北电力大学"); List<UserSchool> userSchoolList = new ArrayList<>();
userSchoolList.add(userSchool_01);
userSchoolList.add(userSchool_02); UserRichInfo userRichInfo = new UserRichInfo();
userRichInfo.setUserInfo(userInfo);
userRichInfo.setUserSchoolList(userSchoolList); return userRichInfo;
} }

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

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

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

/**
* 虫洞栈:https://bugstack.cn
* 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
* Create by fuzhengwei on @2019
*/
@Repository("userRepository")
public class UserRepository implements IUserRepository { @Resource(name = "userMysqlRepository")
private IUserRepository userMysqlRepository; @Resource(name = "userRedisRepository")
private IUserRepository userRedisRepository; @Override
public void save(UserEntity userEntity) {
//保存到DB
userMysqlRepository.save(userEntity); //保存到Redis
userRedisRepository.save(userEntity);
} @Override
public UserEntity query(Long id) { UserEntity userEntityRedis = userRedisRepository.query(id);
if (null != userEntityRedis) return userEntityRedis; UserEntity userEntityMysql = userMysqlRepository.query(id);
if (null != userEntityMysql){
//保存到Redis
userRedisRepository.save(userEntityMysql);
return userEntityMysql;
} // 查询为NULL
return null;
} }

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

/**
* 虫洞栈:https://bugstack.cn
* 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
* Create by fuzhengwei on @2019
*/
public class UserInfoDto { private Long id; // ID public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} }

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

/**
* 虫洞栈:https://bugstack.cn
* 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码
* Create by fuzhengwei on @2019
*/
@Controller
public class DDDController { @Resource(name = "userService")
private UserService userService; @RequestMapping("/index")
public String index(Model model) {
return "index";
} @RequestMapping("/api/user/queryUserInfo")
@ResponseBody
public ResponseEntity queryUserInfo(@RequestBody UserInfoDto request) {
return new ResponseEntity<>(userService.queryUserInfoById(request.getId()), HttpStatus.OK);
} }

六、综上总结

  • 以上基于DDD一个基本入门的结构演示完成,实际开发可以按照此模式进行调整。
  • 目前这个架构分层还不能很好的进行分离,以及层级关系的引用还不利于扩展。
  • 后续会持续完善以及可以组合搭建RPC框架等,让整个架构更利于互联网开发。

七、推荐阅读

Java开发架构篇《初识领域驱动设计DDD落地》的更多相关文章

  1. Java开发架构篇:领域驱动设计架构基于SpringCloud搭建微服务

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 微服务不是泥球小单体,而是具备更加清晰职责边界的完整一体的业务功能服务.领域驱动 ...

  2. 基于领域驱动设计(DDD)超轻量级快速开发架构(二)动态linq查询的实现方式

    -之动态查询,查询逻辑封装复用 基于领域驱动设计(DDD)超轻量级快速开发架构详细介绍请看 https://www.cnblogs.com/neozhu/p/13174234.html 需求 配合Ea ...

  3. python 全栈开发,Day116(可迭代对象,type创建动态类,偏函数,面向对象的封装,获取外键数据,组合搜索,领域驱动设计(DDD))

    昨日内容回顾 1. 三个类 ChangeList,封装列表页面需要的所有数据. StarkConfig,生成URL和视图对应关系 + 默认配置 AdminSite,用于保存 数据库类 和 处理该类的对 ...

  4. 领域驱动设计(DDD)

    领域驱动设计(DDD)实现之路 2004年,当Eric Evans的那本<领域驱动设计——软件核心复杂性应对之道>(后文简称<领域驱动设计>)出版时,我还在念高中,接触到领域驱 ...

  5. 关于领域驱动设计 DDD(Domain-Driven Design)

    以下旨在 理解DDD. 1.     什么是领域? 妈妈好是做母婴新零售的产品,应该属于电商平台,那么电商平台就是一个领域. 同一个领域的系统都有相同的核心业务. eg: 电商领域都有:商品浏览.购物 ...

  6. 领域驱动设计(DDD:Domain-Driven Design)

    领域驱动设计(DDD:Domain-Driven Design) Eric Evans的"Domain-Driven Design领域驱动设计"简称DDD,Evans DDD是一套 ...

  7. Java开发架构篇:DDD模型领域层决策规则树服务设计

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层,但是仅 ...

  8. 如何使用ABP进行软件开发(2) 领域驱动设计和三层架构的对比

    简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项目中涉及到的知识点,例如DTO,应用服务层,整洁架构,领域对象(如实体,聚合,值对象)等. 笔者也曾经提到,AB ...

  9. 基于领域驱动设计(DDD)超轻量级快速开发架构

    smartadmin.core.urf 这个项目是基于asp.net core 3.1(最新)基础上参照领域驱动设计(DDD)的理念,并参考目前最为了流行的abp架构开发的一套轻量级的快速开发web ...

随机推荐

  1. python工业互联网监控项目实战4—python opcua

    前面章节我们采用OPC作为设备到上位的信息交互的协议,本章我们介绍跨平台的OPC UA.OPC作为早期的工业通信规范,是基于COM/DCOM的技术实现的,用于设备和软件之间交换数据,最初,OPC标准仅 ...

  2. (四)PL/SQL运算符

    运算符是一个符号,告诉编译器执行特定的数学或逻辑操作. PL/SQL语言有丰富的内置运算符,运算符提供的以下几种类型: 1.算术运算符 2.关系运算符 3.比较运算符 4.逻辑运算符 5.字符串运算符 ...

  3. python字符串 提取括号中的内容

    返回值是一个列表 re.findall(r'[(](.*?)[)]', str1)

  4. Linux操作系统进入单用户模式的方法

    单用户模式的作用 在使用Linux的过程中,维护人员经常会碰到一些问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况. 遇到这种情况,一般情况下,维护人员就会通过最常用的 ...

  5. PCA主成分分析(上)

    PCA主成分分析 PCA目的 最大可分性(最大投影方差) 投影 优化目标 关键点 推导 为什么要找最大特征值对应的特征向量呢? 之前看3DMM的论文的看到其用了PCA的方法,一开始以为自己对于PCA已 ...

  6. XEP-0199 XMPP Ping

    原文来自:https://xmpp.org/extensions/xep-0199.html,只翻译了技术方面的内容. 摘要:这个规范定义了一个通过XML流发送应用级别pings的XMPP扩展协议.这 ...

  7. 2.Python是什么?使用Python的好处是什么?

    Python是什么?使用Python的好处是什么? 答: Python is a programming language with objects, modules, threads, except ...

  8. 【万字长文】别再报班了,一篇文章带你入门Python

    本文始发于个人公众号:TechFlow,原创不易,求个关注 最近有许多小伙伴后台联系我,说目前想要学习Python,但是没有一份很好的资料入门.一方面的确现在市面上Python的资料过多,导致新手会不 ...

  9. 图论--2-SAT--poj 3678-Katu Puzzle(模板题)

    Description Katu Puzzle is presented as a directed graph G(V, E) with each edge e(a, b) labeled by a ...

  10. Dubbo(六):zookeeper注册中心的应用

    Dubbo中有一个非常本质和重要的功能,那就是服务的自动注册与发现,而这个功能是通过注册中心来实现的.而dubbo中考虑了外部许多的注册组件的实现,zk,redis,etcd,consul,eurek ...