Skip to content

应用服务 vs 领域服务 = 用例编排 vs 业务规则 ​

一、业务问题 ​

DDD 团队最常混淆的:应用服务(Application Service) 和 领域服务(Domain Service)。

错误 1:所有逻辑都在应用服务
  - 应用服务 = 业务逻辑(应该是领域层)
  - 实体 = 数据结构(应该是充血)
  - 等于"面向过程"
  
错误 2:把领域服务当应用服务
  - 领域服务 = 业务规则(但被用作用例编排)
  - 缺少事务管理
  - 缺少权限校验

Vernon 在 Ch 14 明确区分——这是 DDD 分层的核心。

二、控制论视角 ​

应用层 = 控制器(接收目标、调节被控对象)
领域层 = 被控对象 + 控制律

类比(更精确的对位 = Ch 9 §9.4 控制器-控制律):
  - 应用服务 = 控制器(接收目标、选择控制律)
  - 领域服务 = 控制律(业务规则)
  - 实体 = 被控对象(状态)
  - 仓储 = 测量装置(读出状态)
  
Ch 4 §4.6 反馈更偏向"测量→反馈→调节"的链路(应用服务是反馈入口)
Ch 9 §9.4 控制器-控制律更偏向"接收目标→选择控制律→执行"

Vernon 的两类服务精确对位控制论的两层:

  • 应用服务 = 控制器(外部接口)
  • 领域服务 = 控制律(业务实现)

钱学森 Ch 9 §9.4(最优控制):

"在进行控制设计时,首先重要的是搞清楚最优控制是否存在"——应用服务要 justify 调用哪个控制律(领域服务)。

钱学森 Ch 4 §4.6(反馈决定稳定):

"没有反馈,就没有自动控制"——应用服务是"反馈的入口"。

3.1 应用服务(Application Service) ​

位置:应用层
职责:用例编排
状态:无状态
依赖:仓储、领域服务、基础设施
方法粒度:用例级别(动词性)
事务边界:在此管理

特点:
  - 薄
  - 不含业务规则
  - 管理事务、安全
  - 协调多个对象

3.2 领域服务(Domain Service) ​

位置:领域层
职责:业务规则
状态:无状态
依赖:实体、值对象
方法粒度:业务级别(领域操作)
事务边界:不管理

特点:
  - 厚(业务逻辑)
  - 涉及多个实体
  - 不属于任何实体的业务
  - 用 Ubiquitous Language 命名

3.3 对比表 ​

维度应用服务领域服务
职责用例编排业务规则
位置应用层领域层
状态无状态无状态
依赖仓储、领域服务、基础设施实体、值对象
方法粒度用例(动词)业务(领域操作)
事务边界在此管理不管理
薄/厚薄(< 30 行)厚(业务规则)
命名技术性(SaveUser)业务性(RegisterUser)

四、典型反模式 ​

4.1 反模式 1:贫血应用 + 厚应用服务 ​

python
# ❌ 贫血实体 + 厚应用服务
class Order:
    id = None
    items = []
    status = None

class OrderApplicationService:
    def place_order(self, customer_id, items):
        # 业务逻辑全在应用服务
        if not customer_id:
            raise ValueError("Customer required")
        order = Order()
        order.id = generate_id()
        order.customer_id = customer_id
        order.items = items
        order.status = "NEW"
        # 业务规则在应用服务(错误位置)
        if sum(item.price for item in items) > 10000:
            raise ValueError("Order too large")
        self.order_repo.save(order)

问题:

  • 业务逻辑散落在应用服务
  • 实体是数据容器
  • 不能复用业务规则
  • 测试困难

4.2 反模式 2:把领域服务当应用服务 ​

python
# ❌ 领域服务做用例编排
class TransferMoneyService:
    def transfer(self, from_account_id, to_account_id, amount):
        # 业务规则(应该)
        if not self._is_valid_transfer(from_account_id, amount):
            raise BusinessRuleViolation("Invalid transfer")
        
        # 事务管理(应用层职责)
        with self.transaction_manager:
            from_account = self.account_repo.find_by_id(from_account_id)
            to_account = self.account_repo.find_by_id(to_account_id)
            from_account.withdraw(amount)
            to_account.deposit(amount)
            self.account_repo.save(from_account)
            self.account_repo.save(to_account)
            # 权限校验(应用层职责)
            if not self.security.has_permission(...):
                raise PermissionDenied()
            # 事件发布(应用层职责)
            self.event_publisher.publish(...)

问题:

  • 领域服务做了应用层的事
  • 业务规则和事务/权限混在一起
  • 难以测试

五、正模式 ​

5.1 薄应用服务 + 厚领域 ​

python
# ✓ 应用服务:只做编排
class TransferMoneyApplicationService:
    def transfer(self, command: TransferMoneyCommand):
        # 1. 加载聚合
        from_account = self.account_repo.find_by_id(command.from_account_id)
        to_account = self.account_repo.find_by_id(command.to_account_id)
        
        # 2. 业务规则(委托给领域服务)
        self.transfer_service.transfer(from_account, to_account, command.amount)
        
        # 3. 持久化
        self.account_repo.save(from_account)
        self.account_repo.save(to_account)
        
        # 4. 事件发布
        self.event_publisher.publish(
            MoneyTransferred(from_account.id, to_account.id, command.amount)
        )

# ✓ 领域服务:只做业务规则
class TransferMoneyDomainService:
    def transfer(self, from_account, to_account, amount):
        # 业务规则
        if not from_account.can_withdraw(amount):
            raise BusinessRuleViolation("Insufficient funds")
        if from_account.currency != to_account.currency:
            raise BusinessRuleViolation("Currency mismatch")
        # 执行业务操作
        from_account.withdraw(amount)
        to_account.deposit(amount)

六、判断标准 ​

6.1 何时用应用服务 ​

信号:
  - 接收外部请求(UI/API/消息)
  - 协调多个对象
  - 需要事务管理
  - 需要权限校验
  - 需要事件发布
  - 用例粒度("创建一个订单")

6.2 何时用领域服务 ​

信号:
  - 业务规则涉及多个实体
  - 业务规则不属于任何实体
  - 需要 Ubiquitous Language 表达
  - 不需要事务/权限

6.3 何时用实体方法 ​

信号:
  - 业务规则只涉及一个实体
  - 实体的自然行为
  - 应该下沉到实体

七、工程实践 ​

实战 1:判断逻辑放在哪 ​

问 3 个问题:
  1. 这段逻辑涉及几个对象?
     - 1 个 → 实体方法
     - 多个 → 领域服务
  2. 这段逻辑是业务流程还是业务规则?
     - 流程 → 应用服务
     - 规则 → 领域服务/实体
  3. 这段逻辑需要事务/权限吗?
     - 是 → 应用服务
     - 否 → 领域服务/实体

实战 2:应用服务的标准模式 ​

python
class StandardApplicationService:
    def execute(self, command):
        # 1. 加载
        aggregate = self.repo.find_by_id(command.id)
        # 2. 业务(委托)
        aggregate.do_something(command.param)
        # 3. 持久化
        self.repo.save(aggregate)
        # 4. 事件
        self.event_publisher.publish_all(aggregate.events)

实战 3:领域服务的标准模式 ​

python
class StandardDomainService:
    def business_operation(self, entity1, entity2, ...):
        # 1. 业务规则校验
        self._validate_invariants(...)
        # 2. 业务操作
        entity1.modify(...)
        entity2.modify(...)
        # 3. 返回(可选)
        return result

八、性能指标 ​

指标衡量
应用服务行数平均 < 30 行(薄)
领域服务行数业务复杂度决定(可厚)
业务逻辑下沉率多少业务逻辑在领域层 vs 应用层
贫血实体率应该 0

九、边界判断 ​

何时用应用服务:

  • 任何外部入口
  • 任何用例
  • 跨 BC 协调

何时用领域服务:

  • 跨实体的业务规则
  • 不属于任何实体的业务

何时用实体方法:

  • 单个实体的行为

十、与其他 L 层方法论的关系 ​

方法论关系
domain-service-event领域服务基础
aggregate实体方法 vs 领域服务
saga跨 BC 编排
cqrs-es应用服务 vs 命令处理
tell-dont-ask-demeter行为下沉到实体

十一、钱学森原话 ​

Ch 4 §4.6(反馈决定稳定): "没有反馈,就没有自动控制"——应用服务是"反馈的入口"。

Ch 6 §6.7(耦合取舍): "有些耦合作用是有益的"——应用服务耦合用例是必要的。

十二、总结 ​

应用服务 vs 领域服务 = 用例编排 vs 业务规则:

服务职责位置
应用服务用例编排应用层
领域服务业务规则领域层

核心原则:

  • 薄应用 + 厚领域(反贫血)
  • 应用服务管理事务/权限(基础设施)
  • 领域服务只管业务规则
  • 能下沉到实体的不下沉到服务

一句话总结:应用服务编排用例,领域服务执行业务,实体承担状态。

从名家方法论与工程化思路中蒸馏出自己的工程体系。