全部版块 我的主页
论坛 数据科学与人工智能 数据分析与数据科学 python论坛
266 0
2025-12-02

自Eric Evans提出领域驱动设计(Domain-Driven Design,简称DDD)以来,这一方法论逐渐成为应对复杂业务系统的重要指导思想。它不仅仅是一种技术架构模式,更代表了一种以业务为核心、围绕真实世界问题建模的软件设计理念。在当前微服务与分布式架构盛行的背景下,理解DDD相较于传统分层架构的优势及其落地方式,对于构建高内聚、低耦合且具备长期演进能力的系统具有重要意义。

1. DDD的核心理念:为何需要它?

DDD的诞生源于一个深刻认知:

系统的复杂性主要不来自技术实现,而是源自业务领域本身的错综复杂。

当业务规则频繁变更、逻辑交织难解时,传统的数据表驱动或简单分层架构往往会导致业务逻辑分散于Service层、数据库存储过程甚至前端校验中,最终形成难以维护的“大泥球”式代码结构。随着时间推移,代码与实际业务语义渐行渐远。

为解决此问题,DDD主张建立一套

统一的领域模型,作为开发人员与业务专家之间的共同语言(即“通用语言”),并将该模型作为整个系统设计的基础。所有技术实现——包括数据库设计、框架选型和用户界面——都应服务于这个模型,而非主导其形态。

为了清晰展现DDD与传统架构的本质区别,以下从多个维度进行对比分析:

维度 传统分层架构(如MVC、经典三层) 领域驱动设计(DDD) 核心差异解析
设计驱动力 数据与技术。通常以数据库表结构为起点,逐层向上构建CRUD接口。 业务与领域。从真实的业务概念、流程和规则出发,向下映射到技术实现。 DDD是业务驱动,而传统架构多为数据/技术驱动
核心关注点 聚焦于数据的增删改查(CRUD)操作以及各技术层职责的分离。 强调领域模型的完整性、业务规则的显性表达及领域逻辑的高度内聚。 DDD追求的是业务逻辑的纯粹性和集中管理,传统架构则侧重于技术层面的职责划分
业务逻辑位置 逻辑常被分散在Service层、数据库触发器、存储过程甚至前端代码中。 高度集中于领域层中的实体、值对象和领域服务之中。 DDD通过将业务逻辑收拢至领域层,实现了对复杂性的根本性控制。
与数据库关系 模型与数据库强绑定,常表现为数据库表的直接映射(贫血模型)。 通过仓储(Repository)模式实现解耦,领域模型独立于持久化机制。 DDD利用仓储抽象屏蔽持久化细节,实现领域与基础设施的松耦合
应对复杂性的方式 依赖更细粒度的技术分层或引入新框架来缓解混乱。 采用限界上下文(Bounded Context)对大型领域进行战略拆分,降低认知负担。 DDD提供了战略性分解工具,支持从业务边界角度划分系统。
适用场景 适用于业务逻辑简单、以数据管理为主的内部系统或原型项目。 特别适合业务复杂、需长期迭代演进的核心商业系统。 DDD在复杂系统中价值突出,在简单场景下可能造成过度设计。

2. DDD的四层架构详解

经典的DDD采用垂直分层结构,划分为四个清晰层次,每层有明确职责并遵循严格的依赖方向,确保领域核心不受外部技术细节干扰。

- src
    - it    集成测试模块
        - java      集成测试代码
        - resources 集成测试配置文件 
    - test  单元测试模块
        - java      单元测试代码
    - main  业务代码
        - java
            - interfaces    用户接口层
                - facade    提供较粗粒度的调用接口,将用户请求委托给一个或多个应用服务进行处理
                    - rest  REST API
                    - dubbo  
                -  subscribe mq 事件订阅    
                 注1:统一返回Result
                 注2:应该捕捉所有异常         
            - application   应用层        
                 - assembler 实现 DTO 与领域对象之间的相互转换和数据交换                   
                - event     存放事件相关代码,为了事件统一管理,将所有事件发布和订阅统一放到应用层,核心业务逻辑放到领域层
                    - publish   事件发布
                - service 对领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度服务
                    - command   操作相关,必须调用领域层  
                    - query     只放查询相关,可以直接调用持久层  
                 注1:出参必须为 DTO 
                 注2:入参为 Command 或 Query,唯一例外是单ID查询的场景
                 注3:Command 和 Query 有语义作用,避免复用   
                 注4:通过 Spring Validation 来实现入参校验,减少非业务代码混杂业务代码中         
            - domain         领域层
                - aggregate 聚合目录,按业务名称命名,如权限聚合
                    - entity 领域对象
                        - factory   从其他服务返回结果构建领域对象???
                        - valueobject
                    - event 存放事件实体和相关的业务逻辑代码
                    - service  存放领域服务代码 
                    - repository  仓储,存放所有查询和持久化领域对象的代码,通常包括仓储接口和实现,仓储实现一般放在基础层,也可以直接放一起                  
            - infrastructure 基础层   
                - config    存放配置相关代码
                - client    存放跨服务接口、DTO
                  - service   
                  - dto   存放 dto 
                    - command
                    - query
                    - response      
                - common    存放消息、数据库、缓存、文件、总线、网关、公用的常量、枚举等
                    - enums     存放枚举
                    - cache     缓存相关服务
                    - mq        mq相关配置 
                    - db        数据库相关 
                       - mapper  存放 mybatis dao 实现                     
                       - repositories 仓储实现
                            - po            持久化对象
                            - converter     用于封装底层,实现PO与DO一对多或多对多转换 
                            - perisistence  存放 RepositoryImpl,调用 mapper 
                    - ......         
                - util      存放平台、开发框架、第三方类库、通用算法等基础代码
           
        - resources 配置文件

典型工程目录结构如下所示:

变更追踪示例:订单与其明细对象中,仅修改某一条明细项,其余数据保持不变。

场景:非聚合根实体变更操作 
    修改订单明细中其中一条价格,这条明细和订单总价需要变动,其他信息不变  
    持久化到数据库时,只需要修改 orderItem_x 条记录和 order 记录即可
贫血模式:    
    会记录对应的item id ,根据这个ID 更新
充血模式:    
    需要自己做对比,变更跟踪
解决方案:(待测试)   
    aggregate-persistence

各层职责深度剖析

用户界面层(Interface/API Layer)

职责:负责向终端用户或外部系统展示信息,并接收输入指令。本层不包含任何业务逻辑。

关键组件:

  • Controller / REST API:接收请求,解析参数,调用应用服务,并返回DTO结果。
  • DTO(Data Transfer Object):专用于接口传输的扁平化数据结构,与内部领域模型隔离。
  • ViewModel:将领域对象转换为适合前端展示的数据格式。

示例:Web应用的控制器、移动端UI组件、RESTful接口等。

代码示例:

java
// Spring Boot Controller示例
@RestController
@RequestMapping("/orders")
public class OrderController {
    private final OrderApplicationService appService;
    
    @PostMapping
    public ResponseEntity<OrderDTO> createOrder(@RequestBody CreateOrderRequest request) {
        // 1. 参数基本校验(如非空)
        // 2. 调用应用服务,传入纯数据参数
        OrderDTO orderDTO = appService.createOrder(request.getUserId(), request.getItems());
        // 3. 返回给前端的DTO
        return ResponseEntity.ok(orderDTO);
    }
}

应用层(Application Layer)

职责:协调领域对象完成具体的用户用例(User Case)。如同乐队指挥,本身不执行具体演奏(即不包含核心业务规则),只负责流程调度。

关键组件:

  • 应用服务(Application Service):提供细粒度的服务方法,通常每个方法对应一个用户操作。
  • 用例协调:组合多个领域对象或调用领域服务,处理事务边界和安全认证等横切关注点。
  • 事务管理:保证跨多个领域操作的原子性(例如使用Spring的声明式事务)。
  • 权限校验:验证用户身份与访问权限(如JWT鉴权)。

示例:用户注册流程、订单支付流程等典型业务场景。

代码示例:

java
@Service
@Transactional // 事务管理在此层
public class OrderApplicationServiceImpl implements OrderApplicationService {
    private final OrderRepository orderRepository;
    private final InventoryService inventoryService; // 领域服务
    
    @Override
    public OrderDTO createOrder(String userId, List<OrderItemRequest> items) {
        // 1. 协调流程:检查库存(调用领域服务)
        if (!inventoryService.isSufficient(items)) {
            throw new InsufficientInventoryException();
        }
        // 2. 创建领域对象(工厂模式)
        Order order = Order.create(userId, items);
        // 3. 调用领域对象行为(此时业务逻辑在Order实体内部)
        order.confirm();
        // 4. 持久化(调用基础设施层的仓储接口)
        orderRepository.save(order);
        // 5. 发布领域事件(可选,用于跨限界上下文通信)
        domainEventPublisher.publish(new OrderConfirmedEvent(order.getId()));
        // 6. 返回组装好的DTO
        return OrderAssembler.toDTO(order);
    }
}
@Transactional

领域层(Domain Layer)

职责:作为DDD的心脏部分,承载全部业务逻辑,体现业务领域的核心概念、规则、状态变迁与行为流程。

关键组件:

  • 实体(Entity):具有唯一标识且生命周期可追踪的对象,其状态随时间变化(如OrderUser)。
  • 值对象(Value Object):描述某种属性特征但无独立身份的对象,通常不可变(如AddressMoney)。
  • 聚合(Aggregate) & 聚合根(Aggregate Root):一组具有强一致性的关联对象集合,外部只能通过聚合根访问内部成员,确保业务一致性。
  • 领域服务(Domain Service):当某个操作涉及多个实体或无法归属于单一对象时,由领域服务封装该逻辑。
  • 领域事件(Domain Event):表示领域中已发生的重要事实,可用于触发后续动作或实现事件溯源。
Order
User
Money
Address

3、DDD(领域驱动设计)专用术语

1. 通用语言(Ubiquitous Language)
定义:开发、产品与业务人员共同使用的统一术语体系,用于消除沟通中的歧义,确保各方对业务概念的理解一致。
示例:在电商系统中使用“购物车”而非代码化的“CartDTO”,用“优惠券”代替技术性表述“PromotionCode”。

2. 限界上下文(Bounded Context)
定义:将复杂的系统划分为多个具有明确边界的业务区域,每个区域内拥有独立的模型和术语。
示例:一个电商平台可被拆分为“用户上下文”、“订单上下文”、“支付上下文”等独立模块,各自维护自身的逻辑与数据结构。

3. 聚合根(Aggregate Root)
定义:作为聚合的入口点,负责维护其内部实体的一致性和完整性,所有外部操作必须通过聚合根进行。
示例:

Order

作为聚合根,统一管理
OrderItem


Payment

等关联子实体,保证事务边界内的数据一致性。

4. 防腐层(Anti-Corruption Layer, ACL)
定义:在不同限界上下文交互时,防止外部系统的模型污染本地上下文的领域模型,起到翻译与隔离作用。
示例:将第三方支付平台返回的数据结构转换为内部标准化的

PaymentResult

对象,避免直接依赖外部接口。

5. 上下文映射(Context Map)
定义:描述各个限界上下文之间的协作关系,如共享内核、客户-供应商模式或遵奉者模式等。
示例:用户上下文与订单上下文之间采用“共享内核”方式,共用

User

这一核心模型,减少重复建模成本。

4、通用架构术语

1. 高内聚低耦合(High Cohesion, Low Coupling)
定义:模块内部功能高度相关,而模块之间依赖尽可能少,提升系统的可维护性与扩展性。
示例:领域层仅依赖基础设施层定义的抽象接口,而不绑定具体数据库实现。

2. 依赖倒置(Dependency Inversion)
定义:高层模块不应依赖低层模块,二者都应依赖于抽象;抽象不应依赖细节,细节应依赖抽象。
示例:应用服务调用仓储时依赖的是

UserRepository

接口,而非具体的MySQL或MongoDB实现。

3. CQRS(命令查询职责分离)
定义:将写操作(Command)与读操作(Query)分离,使用不同的模型处理不同类型的请求。
示例:创建订单走完整的领域模型流程,而查询订单信息则从缓存或专用读模型中获取,提高性能。

4. 事件溯源(Event Sourcing)
定义:不直接保存当前状态,而是通过一系列不可变的事件来重建对象的状态。
示例:订单的最终状态由

OrderCreatedEvent


OrderPaidEvent

等多个事件依次回放还原而成。

5. 最终一致性(Eventual Consistency)
定义:系统允许短时间内存在数据不一致,但经过一段时间后所有副本将达到一致状态。
示例:在分布式环境下,用户完成支付后,库存扣减可能略有延迟,属于典型最终一致性场景。

4. 战略设计与战术设计:DDD的两大支柱

4.1 战略设计:划分问题空间
战略设计聚焦于从业务角度对系统进行高层次划分,其核心工具是限界上下文(Bounded Context)。例如,在一个大型电商系统中,可以将其分解为“订单上下文”、“库存上下文”、“支付上下文”以及“物流上下文”。每个上下文拥有独立的通用语言和领域模型,并通过上下文映射机制(如防腐层ACL、事件发布/订阅)实现集成。这种划分方式也是确定微服务边界的关键依据。

4.2 战术设计:构建解决方案
战术设计是在已划定的限界上下文中,运用实体、值对象、聚合、领域服务等模式,逐步构建出精细且可落地的领域模型。它是将战略设计转化为实际代码结构的核心过程,决定了领域逻辑能否被正确表达和长期演进。

仓储接口 (Repository Interface)

定义:提供对领域对象持久化操作的标准定义,接口位于领域层,实现在基础设施层。
示例:商品价格策略、金融风控规则等复杂业务逻辑中,仓储用于加载和保存聚合根。

基础设施层 (Infrastructure Layer)

职责:为上层提供各类通用技术能力的支持,充当系统的“工具箱”,涵盖数据存储、通信、异步处理等功能。

关键组件:

  • DAO/ORM 与仓储实现:基于JPA、Hibernate或MyBatis等框架实现数据访问逻辑,对接数据库。
    OrderRepository
  • 消息中间件客户端:集成Kafka、RabbitMQ等实现异步通信,支持领域事件的发布与消费。
  • HTTP Client:使用Feign、RestTemplate等工具调用外部API服务。
  • 文件存储:支持AWS S3、本地磁盘等方式进行文件上传与管理。
  • 缓存机制:引入Redis等缓存组件提升系统响应速度。
  • 其他实现:包括MySQL数据库、SMTP邮件服务等底层技术支持。

代码示例:
java

@Repository
public class JpaOrderRepository implements OrderRepository {
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public Order findById(Long id) {
        // 复杂情况下,这里需要组装聚合(从多张表查询,重建Order及所有OrderItem)
        return entityManager.find(Order.class, id);
    }
    
    @Override
    public void save(Order order) {
        entityManager.persist(order);
        // 通常会在这里或应用层事务提交后,处理order中暂存的领域事件
        publishEvents(order);
    }
}

代码示例(领域模型核心)

java

// 聚合根:订单
public class Order extends BaseAggregateRoot<Long> {
    private Long id;
    private String orderNumber;
    private CustomerId customerId;
    private OrderStatus status;
    private Money totalAmount;
    private List<OrderItem> items; // 内部实体列表,外部不可直接访问
    
    // 【核心】静态工厂方法,封装创建逻辑
    public static Order create(String userId, List<OrderItemRequest> itemRequests) {
        Order order = new Order();
        order.id = OrderIdGenerator.nextId();
        order.orderNumber = generateOrderNumber();
        order.customerId = new CustomerId(userId);
        order.status = OrderStatus.CREATED;
        order.items = itemRequests.stream()
                .map(req -> new OrderItem(req.getProductId(), req.getQuantity(), req.getPrice()))
                .collect(Collectors.toList());
        order.calculateTotal(); // 内部方法计算总价
        return order;
    }
    
    // 【核心】实体行为方法,封装状态变更规则
    public void confirm() {
        if (this.status != OrderStatus.CREATED) {
            throw new IllegalOrderStateException("Only CREATED orders can be confirmed.");
        }
        this.status = OrderStatus.CONFIRMED;
        // 记录领域事件
        this.registerEvent(new OrderConfirmedEvent(this.id, LocalDateTime.now()));
    }
    
    // 值对象:订单项
    @Getter
    public static class OrderItem {
        private final ProductId productId;
        private final Integer quantity;
        private final Money price;
        private final Money subTotal; // 通过构造器计算,确保不变性
        
        public OrderItem(ProductId productId, Integer quantity, Money price) {
            this.productId = productId;
            this.quantity = quantity;
            this.price = price;
            this.subTotal = price.multiply(quantity); // 业务规则内聚
        }
    }
}

5. DDD的应用场景与决策

适用场景:

  • 业务逻辑高度复杂:当系统涉及大量规则判断、状态流转和校验流程时,DDD有助于理清逻辑层次。
  • 业务需要长期演进:面对频繁变更的需求和大型团队协作,清晰的领域模型能有效降低维护成本。
  • 核心域项目:对于企业核心竞争力相关的系统,投入较高设计成本以换取长期可维护性和扩展性是值得的。

需谨慎或避免的场景:

  • 简单CRUD管理后台:若仅为基础增删改查功能,引入DDD会增加不必要的复杂度。
  • 一次性或短期项目:开发周期短、无长期维护需求的项目,采用轻量级架构更为合适。
  • 技术探索型项目:在业务边界尚不清晰的情况下,应优先验证技术可行性,再考虑引入DDD。

6. 现代架构中的DDD应用

DDD的理念已深度融入当代主流软件架构之中:

  • 微服务架构:限界上下文天然对应微服务的拆分边界,成为服务粒度划分的重要理论支撑。
  • 事件驱动架构:领域事件作为跨上下文或微服务间通信的桥梁,促进松耦合、高扩展性的系统设计。

CQRS(命令查询职责分离)是一种常与领域驱动设计(DDD)结合使用的架构模式。在该模式中,命令端负责处理写操作,依赖完整的领域模型来保障业务一致性;而查询端则专注于读操作,采用经过优化的视图模型以提升查询性能。两者之间通过事件机制实现数据同步。在业务逻辑复杂的系统中,这种分离有效缓解了领域模型复杂性与高并发查询性能之间的冲突。

六边形架构与整洁架构是体现“领域核心独立”思想的典型代表,其设计理念与DDD的分层结构高度契合。这类架构强调将业务逻辑置于应用中心,外部依赖如数据库、UI或第三方服务作为可插拔的外围组件,因而常被用作实现DDD的技术骨架,支撑领域模型的纯粹性与可维护性。

- src
    - it    集成测试模块
        - java      集成测试代码
        - resources 集成测试配置文件 
    - test  单元测试模块
        - java      单元测试代码
    - main  业务代码
        - java
            - interfaces    用户接口层
                - facade    提供较粗粒度的调用接口,将用户请求委托给一个或多个应用服务进行处理
                    - rest  REST API
                    - dubbo  
                -  subscribe mq 事件订阅    
                 注1:统一返回Result
                 注2:应该捕捉所有异常         
            - application   应用层        
                 - assembler 实现 DTO 与领域对象之间的相互转换和数据交换                   
                - event     存放事件相关代码,为了事件统一管理,将所有事件发布和订阅统一放到应用层,核心业务逻辑放到领域层
                    - publish   事件发布
                - service 对领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度服务
                    - command   操作相关,必须调用领域层  
                    - query     只放查询相关,可以直接调用持久层  
                 注1:出参必须为 DTO 
                 注2:入参为 Command 或 Query,唯一例外是单ID查询的场景
                 注3:Command 和 Query 有语义作用,避免复用   
                 注4:通过 Spring Validation 来实现入参校验,减少非业务代码混杂业务代码中         
            - domain         领域层
                - aggregate 聚合目录,按业务名称命名,如权限聚合
                    - entity 领域对象
                        - factory   从其他服务返回结果构建领域对象???
                        - valueobject
                    - event 存放事件实体和相关的业务逻辑代码
                    - service  存放领域服务代码 
                    - repository  仓储,存放所有查询和持久化领域对象的代码,通常包括仓储接口和实现,仓储实现一般放在基础层,也可以直接放一起                  
            - infrastructure 基础层   
                - config    存放配置相关代码
                - client    存放跨服务接口、DTO
                  - service   
                  - dto   存放 dto 
                    - command
                    - query
                    - response      
                - common    存放消息、数据库、缓存、文件、总线、网关、公用的常量、枚举等
                    - enums     存放枚举
                    - cache     缓存相关服务
                    - mq        mq相关配置 
                    - db        数据库相关 
                       - mapper  存放 mybatis dao 实现                     
                       - repositories 仓储实现
                            - po            持久化对象
                            - converter     用于封装底层,实现PO与DO一对多或多对多转换 
                            - perisistence  存放 RepositoryImpl,调用 mapper 
                    - ......         
                - util      存放平台、开发框架、第三方类库、通用算法等基础代码
           
        - resources 配置文件

总结:从技术落地到业务价值升华

领域驱动设计的本质是一场思维范式的转变——它推动团队从传统的“如何存储数据、如何展示页面”转向深入理解“业务本身的运作逻辑与核心规则”。借助一套结构化的模式语言,DDD帮助开发团队在错综复杂的业务环境中建立清晰、精准的模型坐标。

尽管DDD存在学习门槛较高、初期投入较大的挑战,但对于正处于数字化转型关键阶段、面临持续增长的业务复杂度的企业来说,采用DDD是一项具有战略意义的技术投资。它有助于构建出具备可持续演进能力、高质量且贴近业务本质的核心系统。

成功实施DDD的关键在于两方面:一是业务专家与技术团队之间的深度协作,确保模型真实反映业务现实;二是对领域模型进行持续迭代和精细化打磨的耐心与坚持。最终,这种努力将转化为软件系统真正的优势——成为业务敏捷发展的助推器,而非制约其发展的技术负担。

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群