快速理解DDD领域驱动设计架构思想-基础篇 | 京东物流技术团队

1 前言

本文与大家一起学习并介绍领域驱动设计(Domain Drive Design) 简称DDD,以及为什么我们需要领域驱动设计,它有哪些优缺点,尽量用一些通俗易懂文字来描述讲解领域驱动设计,本篇并不会从深层大论述讲解落地实现,这些大家可以在了解入门后再去深层次学习探讨或在后续进阶和高级篇了解,希望通过本文介绍,可以让大家快速了解DDD并有一个基础的认知,DDD本身就是理论的集合,很难在不积累理论情况下来有效的实施DDD,仅仅看一些代码案例后就开搞,最终出来东西也是东施效颦,莫要好高骛远。 最后期望大家在工作中能多思考,如你所负责项目如果用DDD如何设计、以及会面临哪些挑战。

学习了解DDD之前,期望大家可在温顾下以往我们所了解掌握一些知识,努力让自己所学所掌握的内容沉淀下来,推荐阅读系列。

  • Head First 设计模式:基础面向对象概念和重要的设计模式;
  • UML面向对象建模基础:从需求到分析,从分析到设计,从设计到编码,UML都有用武之地
  • 实现领域驱动设计:很厚,更加务实,推荐阅读
  • 领域驱动设计:张逸-DDD开山之作,挺玄幻的,多读几遍受益匪浅;

2 定义与概念

领域驱动设计(DDD)提出是从系统的分析到软件建模的一套方法论。将业务概念和业务规则转换成软件系统中的概念和规则,从而降低或隐藏业务复杂性,使系统具有更好的扩展性,以应对复杂多变的现实业务问题。总结它是一套完整而系统的设计方法、是一种设计思维、一种方法论,并不是"系统架构",一种架构设计原则、思维。

2.1、为什么要使用"领域驱动设计",或者说其用途,应用场景式什么?

  1. 善于处理高复杂业务的产品研发、可帮助我们提炼稳定的产品内核(领域模型中称为核心域);

  2. 通过建模可提高建模高内聚、降低模型间的耦合度,提高系统的可扩展性与稳定性;

  3. 强调团队与领域专家的合作沟通,有助于建立一个沟通良好的团队组织;

  4. 统一设计思想与设计规范,有助于提高团队成员的架构设计能力和面向对象设计能力;

  5. 现有的微服务建构都是遵循领域驱动设计的架构原则;

  6. 如果你负责的软件系统并不复杂,那么,你确实不需要学习领域驱动设计!

2.2、领域驱动设计跟时下流行的架构思维最大的区别是什么?

领域驱动设计的思维是:对象+行为+服务,所有的设计围围绕着对象、行为、服务展开;

时下流行架构设计思维是:基于MVC分层架构进行纵向扩展,分业务模块进行产品横向扩展;

2.2.1. 传统的方案

三层应用架构:数据-应用(业务逻辑层)-展现,通常是以数据位为起点进行数据库分析设计。

  1. 服务层过重,数据模型失血,没东西;

  2. 面条式编程或者面向数据库编程,服务层围绕数据库作业完成业务逻辑,经常一条线撸到底;

  1. 代码一整块一整块的过重,很难扩展复用;
  1. 数据库模型只是数据库映射,没有相关的行为支撑,行为都被上一层Service给完成等了,因此是失血 的领域模型;

2.2.2. 领域驱动方案

架构四层在DDD分层结构中将三层中业务逻辑拆解为应用层和领域层,核心业务逻辑表现下沉到领域层去实现,以业务领域模型为核心建模(面向对象建模),更能体现对现实世界的抽象,其优点如下

  1. 轻服务层+充血的领域模型;
  1. 领域模型封装和实现各自应有的行为,可以认为是一个高内聚、低耦合的组件;

  2. 由于模型集数据与行为于一身,是一种自解释的对象,代码复用性高,业务逻辑清晰明确;

  • 用户界面层:主要职责是通过用户界面向用户显示数据信息,同时解释用户的命令,并把用户的请求发送到应用层。
  • 应用层:通过调用基础设置和领域层完成数据资源操作及业务流程编排,相当于BS层;
  • 领域层:将业务逻辑高度内聚到领域层,所以领域层是整个系统的核心,它只与实际业务相关,不关心任何技术细节,尽可能做到与持久化无关;
  • 基础设施层:包含了任何类型的框架、数据库访问代码或者公共的方法等,纯技术的一层;

2.3、如何学习领域驱动设计

没有谁能够做到领域驱动设计的一蹴而就,所谓"理论联系实际",在刚开始接触或学习设计领域驱动时,总会有一种诉求希望能给出公式般的设计准则或规范,似乎软件设计就像拼积木一般,只要遵循图示给出的拼搭过程,不经思考就能拼出期待的模型,这似乎不切实际的幻想,要掌握领域驱动设计,首先要了解掌握一些概念以知识理论,在此基础之上思考这些概念背后蕴含的原理,设计原则,思考限界上下文(Bounded Context)边界的划分,实际还是围绕"高内聚、低耦合"原则的体现,只是我们考虑什么内容才是高内聚,如何抽象才能做到低耦合,在分层架构中,各层之间该如何协作?出现了依赖如何解耦,仍然需要从重用与变化的角度去思考设计决策。

3 领域驱动设计

领域驱动设计强调以"领域"为核心驱动力,通过模型驱动设计来保障领域模型与程序设计的一致,领域模型不应该包含任何技术实现因素,模型中的对象真实的表达了领域概念,却不受技术实现的约束,领域模型本身和技术无关,领域驱动从设计上划分为战略设计和战术设计。

  • 一个领域是由一个或多个模型组成;
  • 从定义上讲模型是领域的抽象;
  • 从理解上将模型可以认为是一个高内聚、低耦合的组件、模块,也可以称为一个子领域;
  • 模型重在建模过程,建模过程会抽象出一系列领域对象和领域服务;
  • 在DDD中,定义一系列的标准"领域元素"用于领域建模;如战术设计元模型

3.1. 战略设计

强调业务战略上的重点,如何按重要性分配工作,以及如何进行最佳,遵循量大原则:

  1. 必须指导设计决策,以便减少各个部分之间的相互依赖,在使用设计意图更为清晰的同时而又不失去关键的互操作性和系统性;

  2. 必须把模型的重点放在捕获系统概念核心,也就是系统的"远景"上。

3.1.1 子域划分

整个业务领域的一部分,关注与宏观业务,通过对大领域进行划小在业务间划分出概念上分界线,便于在系统筹划阶段决策如何分配资源(人、钱)。

  1. 核心子域:领域中最有价值和最核心的部分,产品成败的关键,核心竞争力,在DDD开发中,主要关注核 心域,给予最高优先级;
  1. 支撑子域:项目中对核心子域起着支撑作用的相关功能,专注于业务的某个反面;

  2. 通用子域:与项目意图无关的内聚子领域,解决一些通用问题,任何专有的业务都不应该放在通用子域;

3.1.2. 战略建模

关注点在于系统物理划分,根据你对领域的分割结果及公司或部门的组织结构决策如何划分子系统,比如分出几个,系统间如何交互,该层面往往暂不会涉及够多技术细节。

  1. 限界上下文(Bounded Context):通俗讲指系统中模块,微服务架构中的子服务、单体中"包(Java)"或名称空间(C#),对系统的一个物理划分,限定了领域模型的边界,在更深层次介绍深挖,这东西得分成两个词:限界、上下文,可以理解成系统设计之初,你需要画一个圈设置一个范围,保证领域模型限制在这个圈内不可串场,这个'圈'即为限界上下文。
  1. 通用语言:用于统一领域专家、产品、研发、测试大家在使用的语言,避免出现需求理解不一致,设计与需求不一致,沟通不顺畅等问题,简单来说大家在一起聊某个东西的时候都能明白彼此所致的是什么,场景不同,同一个词就会有着不同含义。

  2. 上下文映射图:用图的方式,表达出限界上下文之间关联,后续会单独在详细介绍上下文映射图,如 合作关系、防腐层、大泥球等;

3.2. 战术设计

依赖于领域模型和通用预言,通过技术模式将领域模型和通用预言中的概念映射到代码实现中。随着模型的进化,代码实现也会进行重构,以更好的体现模型概念。

  • 主要包括:
  1. 代表领域中的概念,如实体、值对象、领域服务、模块等;

  2. 用于管理对象的生命周期。如聚合、工厂、仓库等;

  3. 用于集成或跟踪,如领域事件等;

3.3. 名词解释

  1. 领域/子域:什么领域?从广义上将,领域即是一个组织所做的事情以及所包含的一切,领域可大可小有界限,不是无限大,如电商领域,交易领域,购物领域等,比如我们常听客户说"我们有这样几块业务"一般来说这里所谓"几块儿"就是指子域 。

  2. 实体(entity):这个词被我们广泛使用,甚至过分使用,实体是一个重要的概念,一个典型实体应具备3个要素(身份标识、属性、领域行为),必须有唯一的身份标识,没有身份标识的领域对象就不是实体。如:User对象就是一个实体。

  3. 属性:实体的属性用来说明主体的静态特征,并持有数据与状态。

    @Data
    public class Product{
    private String sku;
    private String name;
    private Price price;
    }

  4. 领域行为:实体拥有领域行为,可以更好地说明其作为主体的动态特征。一个不具备动态特征的对象不属于领域行为。

    @Data
    public class Product{
    private String sku;
    private String name;
    private Price price;

    复制代码
     //变更状态的领域行为
     public void changePriceTo(Price newPrice){
         //设计产品新加个
         .......
         
     }

    }

  5. 值对象(value object):比较抽象,通常作为实体的属性,区分值对象与实体的区别在于,值对象是不可变的,并且没有唯一标识,仅由其属性的值定义,参与则对它的判断是依据值还是依据身份标识,前者是值对象,后者是实体;

举个小例子:订单项和订单的关系:多对一,一个订单里有多条订单项,一个订单项,只会出现在一个订单里,组合关系,部分不能脱离主体单独存在

复制代码
public class Order {
    int id;
    User user;
}

public class OrderItem {
    private int id;
    private Product product;
    private int num;
    private Order order;
}
  1. 聚合根:聚合中需要指定一个实体作为聚合根来作为整个聚合的对外触电,也就是说外部只能通过聚合根实现对内部对象的访问,这样的限制可以对内部对象实现最大化的保护。

4 价值是什么

几乎所有项目的发展都有这样一个规律:初期需求简单,中后期业务激增系统复杂度升级,导致最初的设计理念需要大刀阔斧的改革,所以,系统越复杂、代码规模越大,DDD 的优势就越明显。

  • 合作沟通:强调团队与领域专家的合作沟通,有助于建立一个沟通良好的团队组织;
  • 统一思想:统一设计思想与设计规范,有助于提高团队成员的架构设计能力和面向对象设计能力;
  • 系统灵活:通过建模可提高模型的高内聚,降低模型建的耦合度,提高系统的可扩展性与稳定性;
  • 产品内核:善于处理高复杂度业务产品研发,可帮助我们提炼稳定的产品内核;
  • 业务沉淀:领域模型是系统的核心,是领域内业务的直接沉淀,具有非常大的业务价值。

5 基础篇结束语

微服务划分的一个重要理论基础就是领域驱动设计,但由于DDD门槛高、概念多,体系庞大又抽象,再加上实践经验和案例缺少,很多开发人员对DDD存在不少疑惑,或只停留在平时依靠检索或身边同事谈及耳闻了解DDD,通过本篇初步认识了领域驱动设计、前期我们先暂短介绍这里,后续会将从代码层面入手分享DDD实现落地。

作者:京东物流 边雷

来源:京东云开发者社区 自猿其说Tech 转载请注明来源

相关推荐
coderSong25682 小时前
Java高级 |【实验八】springboot 使用Websocket
java·spring boot·后端·websocket
Mr_Air_Boy3 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
打码人的日常分享4 小时前
物联网智慧医院建设方案(PPT)
大数据·物联网·架构·流程图·智慧城市·制造
咖啡啡不加糖4 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
白水baishui4 小时前
搭建强化推荐的决策服务架构
架构·推荐系统·强化学习·决策服务·服务架构
何双新4 小时前
第23讲、Odoo18 邮件系统整体架构
ai·架构
雪碧聊技术4 小时前
将单体架构项目拆分成微服务时的两种工程结构
微服务·架构·module·project·工程结构
大鸡腿同学4 小时前
纳瓦尔宝典
后端
从零开始学习人工智能5 小时前
Doris 数据库深度解析:架构、原理与实战应用
数据库·架构