Skip to content

单写者原则 = 聚合边界的工程实现 ​

解决什么问题:分布式系统中同一聚合被多个服务写入导致冲突—— 单写者原则强制"一个聚合 = 一个写者",让冲突从源头上消失。

业务问题 ​

真实场景 ​

场景 1:电商订单被多个服务修改

python
# 反模式:多个微服务修改同一聚合
class OrderService:
    def update_status(self, order_id, status):
        # 订单服务写
        ...
class ShippingService:
    def mark_shipped(self, order_id):
        # 物流服务也写同一订单
        ...
class PaymentService:
    def mark_paid(self, order_id):
        # 支付服务也写同一订单
        ...

# 问题:
# - 三个服务都能写 Order 聚合 → 分布式写冲突
# - 谁先谁后?最后写赢?
# - 不变量(订单状态机)谁保证?

Bellemare Ch 2 原话:

"Microservice Single Writer Principle... One microservice is solely responsible for each aggregate... Other services must consume and react, not directly modify."

失败模式 ​

失败模式后果
多服务写同一聚合分布式冲突;状态不一致
应用层双写DB 和事件流不一致(无事务保证)
共享数据库写紧耦合;破坏 BC 边界

控制论视角 ​

Ch 6 §6.3:"解耦" + Ch 4 §4.6:"不变量必须被维护"

单写者 = 控制律的唯一执行者
  - 一个聚合 = 一个控制律 = 一个执行者
  - 不变量 = 受控状态的稳定条件(只有一个执行者能维护)
  - 状态反馈 = 只有执行者能接收并响应

对位:

  • 写者 = 控制律的执行器(受控对象的唯一"手")
  • 消费者 = 观测器(只能看,不能改)
  • 事件流 = 反馈通道(观测结果通过事件流传递)

核心概念 ​

单写者原则 ​

原则:
  一个聚合类型 = 一个微服务 = 唯一写者
  
约束:
  - 只有该微服务可以生产该聚合的事件
  - 其他微服务只能消费,不能直接修改
  - 该微服务持有该聚合的所有状态变更权

单写者 vs 多写者 ​

维度单写者多写者
冲突无高
一致性简单复杂(需 Paxos/Raft)
性能高(无协调)低(协调开销)
可调试易难
适用90% 场景极少数(共享数据)

单写者的实施路径 ​

步骤 1:识别聚合(按业务边界)
  - 一个 BC 一个聚合根
  - 聚合根包含业务不变量
  
步骤 2:指派写者
  - 一个微服务拥有该聚合
  - 该服务是"真理之源"
  
步骤 3:限制写权限
  - 其他服务消费事件
  - 通过事件流间接影响
  - 不能直接调 API 修改
  
步骤 4:处理读
  - 其他服务需要读 → 通过事件流物化
  - 或通过专门的查询 API

与 IDDD 聚合的关系 ​

IDDD Ch 10 聚合 4 规则:
  - 小聚合(性能)
  - 一致性边界(事务)
  - ID 引用(解耦)
  - 边界外最终一致
  
单写者原则 = 聚合的"运行时实现"
  - 聚合 = 静态边界
  - 单写者 = 动态执行者
  - 二者结合 = 边界清晰 + 责任清晰

工程实践 ​

决策 1:识别聚合边界 ​

决策树:
  多个服务要改同一数据?
    - 是 → 必然违反单写者
    - 否 → 考虑合并服务或拆分聚合
  数据有强不变量?
    - 是 → 必须聚合 + 单写者
    - 否 → 可以独立写

决策 2:处理跨聚合修改 ​

问题:物流服务要修改订单状态

反模式:
  - 物流服务直接调订单 API 修改 → 违反单写者
  
正模式:
  - 物流服务发 OrderShippedEvent 事件
  - 订单服务订阅事件,更新订单状态
  - 订单服务是订单聚合的唯一写者

反模式 vs 正模式 ​

反模式正模式
多服务直接写同一 DB单服务拥有聚合
共享数据库(multi-writer)单写者 + 事件流
同步调用修改数据事件流传递状态变更
应用层双写(DB + Event)CDC 单写(DB 是 SSOT)

与 CDC 配合 ​

模式:单写者 + CDC
  1. 服务 A 写自己的 DB(单写者)
  2. CDC 监听 DB 的 binlog
  3. binlog 转换为事件流
  4. 其他服务消费事件
  
优点:
  - 单写者原则不破坏(DB 写入)
  - 事件流自动生成(无应用双写)
  - 完美解耦

性能指标 ​

指标衡量健康值
单写者覆盖率多少聚合有唯一写者> 95%
跨服务直接写多少写操作跨服务0
冲突率写冲突 / 总写0
CDC 延迟DB 变更 → 事件发出< 1s

边界判断 ​

何时用单写者(★ 必须) ​

  • 有清晰聚合边界
  • 业务有不变量
  • 多个服务可能涉及同一数据

何时不用(极少数) ​

  • 真正的共享数据(如用户偏好)
  • 多主复制(multi-master)刻意设计
  • 单服务内的多线程(不是微服务问题)

何时升级 ​

当前升级到触发条件
应用双写单写者 + CDC频繁不一致
多服务共享 DB单写者 + 事件流BC 边界确立
同步调用改数据事件流性能/解耦需求

关键洞察 ​

  1. 单写者 = 聚合的运行时形态——静态边界(DDD 聚合)+ 动态执行(单写者)= 完整一致性。
  2. 写者唯一 = 冲突从源头消失——比事后协调(分布式锁/事务)简单 100 倍。
  3. 单写者 + CDC = 完美解耦——DB 是 SSOT,事件流是投影,应用不双写。
  4. 与 IDDD 同构——Ch 10 聚合 4 规则的"小聚合"前提就是单写者。
  5. 与 cybernetics 同构——单写者 = 受控对象的唯一执行器 = Ch 4 §4.6 反馈环的唯一接收方。

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