DDD领域驱动设计

本文介绍了DDD领域驱动设计。以及如何进行重构。

DDD领域驱动设计

要知道DDD架构 首先需要知道软件架构的演变之路

一、三种软件架构

1.传统单体架构

所有的应用功能都集成在一个单一的应用程序中,所有模块和组件都在同一个进程内运行,请求直接操作数据库,不进行代码分层,易于开发和部署,尤其适合小型或简单的应用。

随着业务增长和需求变更,单体架构变得难以扩展和维护。不同功能的模块耦合在一起,导致更新某个功能可能影响到整个系统。

2.三层架构

应用被划分为不同的层(如业务接入层、业务逻辑层、数据访问层等),每一层负责特定的功能,层与层之间通过接口进行交互,促进了模块化和职责分离,便于管理和维护。

但层与层之间的紧密耦合限制了灵活性,且随着系统的复杂度增加,可能导致性能下降和维护难度增加,并且它的可扩展性和弹性伸缩性差。

3.微服务架构

将系统拆分为多个小而独立的服务,每个服务负责处理一组特定的功能,每个服务通常由独立的团队开发、部署和维护,服务之间通过轻量级协议(如 HTTP、自定义协议或消息队列)进行通信。

服务之间独立,易于扩展和维护。每个微服务都可以独立部署、开发和扩展,且易于使用不同的技术栈。

二、DDD 领域驱动设计概念

1.什么是DDD

DDD(领域驱动设计,Domain-Driven Design) 是一种软件开发方法论和设计思想。DDD 通过领域驱动设计方法定义领域模型,从而确定业务和应用的边界,保证业务模型和代码模型的一致性。

因为 DDD 主要应用在微服务架构场景,所以想要更好的理解 DDD 的概念,需要结合微服务架构来看:

  • DDD 是一种设计思想,确定业务和应用的边界
  • 微服务架构需要 将系统拆分为多个小而独立的服务

DDD 就是一个方法论,指导我们根据领域模型确定业务的边界,从而划分出应用的边界,最终落实成服务的边界、代码的边界。

总结:因此。每个公司的DDD架构可能会有区别。DDD并没有统一的标准。但是设计思想是一致的。也就说DDD是一种设计思想 。是软件开发的方法论

2. DDD 的目标

  1. 通过领域模型实现业务需求:开发者与领域专家共同理解业务需求,形成共享语言并构建模型。
  2. 提高系统的灵活性与可维护性:通过合理划分限界上下文,减少系统的耦合度,使得不同模块或子系统可以独立演化。
  3. 支持复杂业务逻辑的表达:通过深入的业务建模,使得复杂的业务逻辑能够清晰、准确地反映在代码中。

总结一下,就是让系统更贴合业务,让大型系统更利于独立建设和维护。

3.DDD 建模总结

结合上面的名词解析,我们回顾一下 DDD 建模的流程。

首先我们需要领域建模,此时会进行事件风暴,通过用例分析、场景分析等方式列出所有的业务行为与事件,找出产生这些行为的领域对象,包括实体与值对象。梳理这些领域对象之间的关系,从实体中找出聚合根,再根据聚合根的业务,找寻与其业务紧密关联其它实体与值对象,从而形成聚合。多个聚合之间根据业务相关性又可以划出限界上下文。

可以通过 “开公司” 的比喻来帮助大家理解 DDD。领域就像公司的行业,决定了公司所从事的核心业务;限界上下文是公司内部的各个部门,每个部门有独立的职责和规则;实体是公司中的员工,具有唯一标识和生命周期;值对象是员工的地址或电话等属性,只有值的意义,没有独立的身份;聚合是部门,由多个实体和值对象组成,聚合根(如部门经理)是部门的入口,确保部门内部的一致性;领域服务则是跨部门的职能服务,比如 HR 或 IT 服务,为各部门提供支持和协作。

4.DDD架构设计

1.充血模型和贫血模型

贫血模型和充血模型是两种面向对象设计模式,用于描述对象的职责划分和对象是否包含行为逻辑。

简单来说贫血模型只具备状态(数据)而不具有行为(业务方法)

充血模型既具备状态(数据)又具备行为(业务方法)

以下为充血模型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Order {
    private String orderId;
    private double totalAmount;
    private boolean isPaid;

    public Order(String orderId, double totalAmount) {
        this.orderId = orderId;
        this.totalAmount = totalAmount;
        this.isPaid = false;
    }

    public void pay() {
        if (this.isPaid) {
            throw new IllegalStateException("Order is already paid");
        }
        this.isPaid = true;
    }

    public void cancel() {
        if (this.isPaid) {
            throw new IllegalStateException("Cannot cancel a paid order");
        }
        // Perform cancellation logic
    }

    public boolean isPaid() {
        return isPaid;
    }

    public double getTotalAmount() {
        return totalAmount;
    }
}

以下为贫血模型 在贫血模型中,Order 对象只包含数据(状态)而所有的业务逻辑(如 payOrder 和 cancelOrder)都被移到了外部的 OrderService 服务类中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Order {
    private String orderId;
    private double totalAmount;
    private boolean isPaid;

    public Order(String orderId, double totalAmount) {
        this.orderId = orderId;
        this.totalAmount = totalAmount;
        this.isPaid = false;
    }

    public String getOrderId() {
        return orderId;
    }

    public double getTotalAmount() {
        return totalAmount;
    }

    public boolean isPaid() {
        return isPaid;
    }

    public void setPaid(boolean paid) {
        isPaid = paid;
    }
}

public class OrderService {
    public void payOrder(Order order) {
        if (order.isPaid()) {
            throw new IllegalStateException("Order is already paid");
        }
        order.setPaid(true);
    }

    public void cancelOrder(Order order) {
        if (order.isPaid()) {
            throw new IllegalStateException("Cannot cancel a paid order");
        }
        // Perform cancellation logic
    }
}

2.DDD 的分层架构

在领域驱动设计(DDD)中,分层架构模型是一种常见的设计模式,用于组织和管理系统的复杂性。通过将应用分为不同的层次,每一层都有清晰的责任和角色,从而促进了代码的高内聚、低耦合和可维护性。

DDD 的分层架构主要有四层:用户接口层应用层领域层基础设施层。每层负责不同的职责,协调工作以实现系统的整体功能。

除基础设施层外,严格来说每层只能与 直接下层 产生依赖,即领域层只能被应用层调用,应用层只能被用户接口层调用。

三、DDD重构

改造方法

领域划分

首先要从系统业务出发 看看能分出几个领域 如用户领域 。图片领域

改造步骤

从源代码出发进行构建 而不是按照DDD四层架构一层一层去构建

按照 model => mapper => service => controller去重构

而且一次只重构一个领域

1.基础设施层

因为infrastructure 层是存放基础设施的代码,也就是通用的代码,所以需要要优先重构

2.应用服务层

  • 1.应用服务层需要调用领域服务层 因此在实现类当中应该要引入
  • 2.应用服务层起到编排领域服务层的作用
  • 3.原则上 上层接口只能调用下层接口。下层不能调用上层 因此如果原来的代码中有调用其他领域的方法需要放到应用服务层中
  • 4.应用服务层也可以为其他的应用服务层提供服务 因此一些统一的服务 也要写到应用服务层
  • 5.事务一般也写在应用服务层

总结:应用服务层起到编排领域服务层的作用。只要发现不调用其他应用服务的方法、并且不调用 “当前类中依赖其他应用服务” 的方法,就可以改为调用领域服务;否则该方法需要在应用服务中实现。

3.领域服务层

领域服务层遵循的原则:

  • 需要调用数据库服务(repository)或基础设施层(infrastructure)来完成业务逻辑
  • 可以根据需要,将和实体强相关的业务逻辑下沉到 实体类

4.用户接口层

主要保持精简不具备任何逻辑业务。只调用应用服务。

可以具备转换类

四、总结

其实 DDD 并不是多么“高大上”的知识,实际上有点类似于在传统分层架构的基础上多增加了一层 “应用服务层”,进一步的去细分职责。

因此对于非大型项目来说,反而增加了额外的编码。一定要按需使用

最后更新于 2025-03-20
使用 Hugo 构建
主题 StackJimmy 设计