Skip to content

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

fuzhengwei edited this page May 16, 2020 · 1 revision

作者:小傅哥
博客:https://bugstack.cn - 原创系列专题

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

微信公众号: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

代码示例

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

下一篇:DDD专题案例二《领域层决策规则树服务设计》

微信搜索「bugstack虫洞栈」公众号,关注后回复「DDD落地案例」获取本文源码&更多原创专题案例!

📝 首页

🌏 知识星球码农会锁

实战项目:「DDD+RPC分布式抽奖系统」、专属小册、问题解答、简历指导、架构图稿、视频课程

🐲 头条

⛳ 目录

  1. 源码 - :octocat: 公众号:bugstack虫洞栈 文章所涉及到的全部开源代码
  2. Java
  3. Spring
  4. 面向对象
  5. 中间件
  6. Netty 4.x
  7. 字节码编程
  8. 💯实战项目
  9. 部署 Dev-Ops
  10. 📚PDF 下载
  11. 关于

💋 精选

🐾 友链

建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。

作者:小傅哥
链接:https://bugstack.cn
来源:bugstack虫洞栈

2021年10月24日,小傅哥 的文章全部开源到代码库 CodeGuide 中,与同好同行,一起进步,共同维护。

这里我提供 3 种方式:

  1. 提出 Issue :在 Issue 中指出你觉得需要改进/完善的地方(能够独立解决的话,可以在提出 Issue 后再提交 PR )。
  2. 处理 Issue : 帮忙处理一些待处理的 Issue
  3. 提交 PR: 对于错别字/笔误这类问题可以直接提交PR,无需提交Issue 确认。

详细参考:CodeGuide 贡献指南 - 非常感谢你的支持,这里会留下你的足迹

  • 加群交流 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信(fustack),备注:加群。
微信:fustack

  • 公众号(bugstack虫洞栈) - 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。
公众号:bugstack虫洞栈

感谢以下人员对本仓库做出的贡献或者对小傅哥的赞赏,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。

Clone this wiki locally