广告
您当前的位置: 首页 >  技术 >  编程开发

领域战术核心:解密领域服务(Domain Service)与应用服务(Application Service)的边界

作者:CoderWang 时间:2026-07-07 阅读数:0人阅读

在开展领域驱动设计(DDD)的战术代码编写时,开发人员往往会遇到一个最经典、也最容易混淆的架构设计难题:**“同样是写业务逻辑,到底应该写在领域服务(Domain Service)中,还是写在应用服务(Application Service)中?”**

如果分工边界模糊,会导致大量的核心业务规则上溢到应用服务层,使领域模型退化为“贫血模型”;或者相反,将数据库事务、安全校验、日志记录等底层技术细节下沉到领域服务,导致领域层被技术框架噪声严重污染。

本文将系统拆解领域服务与应用服务在业务规则归属、技术噪声隔离、以及事务控制边界上的本质区别。

一、 核心定位对比:领域服务 vs. 应用服务

两种服务在 DDD 架构分层体系中扮演着截然不同的角色:

特征维度领域服务 (Domain Service)应用服务 (Application Service)
分层归属属于核心**领域层 (Domain Layer)**。属于**应用服务层 (Application Layer)**。
核心职责处理跨越多个聚合根的**纯粹核心业务规则**。不应包含任何技术细节。负责**用例编排(Orchestration)**。充当业务流程的指挥官。
技术框架依赖**绝对零依赖**。不能直接依赖 Spring、JPA 等物理技术框架。允许依赖 Spring Security、事务管理(@Transactional)等。
事务边界管控不负责事务控制。它只执行计算或规则。**事务管理边界(Transaction Boundary)**。开启与提交事务。
典型业务场景如“资金转账”(涉及转出账户与转入账户两个聚合,且必须保证规则一致)。如“用户注册”(包含安全校验、查重、发送短信、落盘事务编排)。
---

二、 业务用例流转与服务协作拓扑

在一个典型的订单付款业务用例中,应用服务与领域服务的相互协作拓扑如下:

      [ 外部控制器 (OrderController) ] ── 1. 发起付款请求 (payOrderCommand) ──> [ 应用服务: OrderAppService ]
                                                                                   │
                                                          ┌────────────────────────┴────────────────────────┐
                                                          ▼ 2. 编排技术细节                                  ▼ 4. 执行业务编排
                                               [ 数据库/安全/事务 ]                                 [ 核心领域服务 ]
                                               - @Transactional 事务开启                     [ OrderPaymentService ]
                                               - 仓储加载: orderRepository.load()                    │
                                                                                                    ├─ 5. 调用聚合根: order.pay()
                                                                                                    ├─ 6. 计算折扣: discountCalculator
                                                                                                    │
                                                          ◄────── 7. 返回结算业务数据 ───────────────┘
                                                          │
                                                          ▼ 8. 保存持久化并提交事务
                                               [ 9. 触发领域事件广播发送短信 ]
---

三、 代码实战:资金转账用例中的服务边界落地

以下通过“资金转账(Transfer)”这一经典跨聚合场景,展示领域服务与应用服务在代码层面的物理隔离规范:

1. 领域层:纯净无污染 of 领域服务(Domain Service)

package com.company.finance.domain.service;

import com.company.finance.domain.model.Account;
import org.springframework.stereotype.Service; // 仅作为 Spring Bean 扫描,不依赖 Spring 特有逻辑

/**
 * 资金转账领域服务
 * 规约:此服务不负责数据库事务,不操作 Repository,只实现跨聚合根的核心转账规则判定
 */
public class TransferDomainService {

    public void transfer(Account source, Account target, double amount) {
        // 1. 验证转出账户余额是否充足(核心业务规则)
        if (!source.canWithdraw(amount)) {
            throw new IllegalArgumentException("转出账户余额不足,转账中止!");
        }
        
        // 2. 执行扣减与累加操作(状态流转)
        source.withdraw(amount);
        target.deposit(amount);
    }
}

2. 应用层:承载技术细节与用例编排的应用服务(Application Service)

package com.company.finance.application.service;

import com.company.finance.domain.model.Account;
import com.company.finance.domain.repository.AccountRepository;
import com.company.finance.domain.service.TransferDomainService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 资金转账应用服务
 * 规约:负责开启数据库事务,装配技术框架,加载数据,并协调领域服务执行
 */
@Service
public class TransferApplicationService {

    private final AccountRepository accountRepository; // 注入基础设施层仓储
    private final TransferDomainService transferDomainService; // 注入领域服务

    public TransferApplicationService(AccountRepository repository, TransferDomainService service) {
        this.accountRepository = repository;
        this.transferDomainService = service;
    }

    /**
     * 转账用例编排
     */
    @Transactional // 开启事务
    public void executeTransfer(String sourceAccountId, String targetAccountId, double amount) {
        // 1. 加载物理数据
        Account source = accountRepository.findById(sourceAccountId);
        Account target = accountRepository.findById(targetAccountId);
        
        // 2. 委托领域服务执行转账业务逻辑
        transferDomainService.transfer(source, target, amount);
        
        // 3. 执行物理落盘保存
        accountRepository.save(source);
        accountRepository.save(target);
    }
}
```---

四、 总结

领域服务(Domain Service)与应用服务(Application Service)的清晰界定,是保障微服务领域层纯度与代码结构整洁的核心铁律。

It 通过**将涉及多聚合根的复杂业务规则强制保留在领域层(Domain Service),守护了核心模型的饱满与可复用性;并将事务管控、数据检索和外部通道隔离在应用层(Application Service),使得核心业务计算不再与底层的 Spring 框架和物理数据库技术产生纠缠**。掌握这套服务边界界定与工程协作逻辑,是杜绝微服务架构退化为贫血大泥潭、编写高可读性 DDD 代码的基本功!

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。

评论交流 (0)

正在加载评论...
头像

CoderWang

当你还撑不起你的梦想时,就要去奋斗。如果缘分安排我们相遇,请不要让她擦肩和过。我们一起奋斗!

微信