跳转到内容

状态机建模

用途:单据/工单类业务反复出现“非法状态变更”缺陷时查阅

状态机用有限的状态集合、事件(或命令)集合与迁移函数描述对象生命周期:当前状态 + 事件 → 下一状态(含守卫条件与副作用)。

在业务系统里,它通常是聚合根的核心资产:单据的复杂度大多不在计算,而在“什么状态下允许什么操作”。

不用显式状态机时的典型形态:多个布尔字段加状态码组合判断(if (status == 3 && !deleted && approved))。三个缺陷:非法组合无法在编译期排除;迁移规则散落在各处,新增状态要全局搜索;审计与回放困难。

显式状态机把迁移合法性集中到一处,使“非法迁移”成为不可能事件而不是运行时意外。

  1. 枚举状态:只列业务上真实存在的状态,避免“临时状态”;区分主状态与子状态(如“已关闭”下的“正常关闭/超时关闭”)。
  2. 枚举事件:来自用户操作、定时任务、外部回执的事件分开标注。
  3. 写迁移表:(源状态, 事件, 守卫) → (目标状态, 动作)。表中未列出的组合即非法。
  4. 定义副作用:进入/退出状态时产出的领域事件、通知、补偿动作。
  5. 落库与审计:状态字段 + 迁移历史(谁、何时、因何事件),支持追溯。
  6. 并发保护:迁移方法在聚合根上,配合版本号做乐观锁,防止并发迁移产生跳跃。
形态 适用 代价
枚举 + 迁移表(代码内 Map/switch) 状态数 < 20、迁移稳定 图与代码需人工同步
状态机框架 迁移复杂、需要监听器与守卫 学习成本、调试间接层
工作流/审批引擎 需要“谁在什么时候处理”的人工任务编排 引擎不承担业务规则,误用会把规则塞进流程配置
事件溯源 需要完整历史与时间旅行 高复杂度,只在有强审计需求时选

关键分工:审批流引擎管“流转与待办”,状态机管“业务状态是否允许该变更”。让流程引擎直接改业务状态字段,是规则失控的常见起点。

开源参考:Spring StateMachine、Koloda(轻量 Java 状态机)、AWS Step Functions / Temporal(长流程编排,属于工作流而非实体状态机)。

误区 修正
用布尔字段拼状态 单一状态枚举 + 迁移表
状态定义包含实现细节(sync_flag=2) 状态是业务概念,不是同步进度
迁移规则散在 Controller/Job/MQ 消费者 收敛到聚合根方法,入口只调用
无迁移历史 记录状态变更事件,支撑审计与排障
并发下双写迁移 乐观锁 + 迁移前重新校验守卫
把流程引擎当状态机 两者职责不同,需明确谁拥有业务状态
  • 聚合根:迁移合法性由根守护,状态是值对象(枚举)。
  • 领域事件:每次成功迁移产出一个事件,供下游订阅与审计。
  • 规范模式:守卫条件适合表达为 Specification,便于组合与测试。
  • 分布式事务:跨系统状态推进常需要 Saga 或最大努力通知兜底。
  • Martin Fowler《企业应用架构模式》(State Strategy 章节)
  • Vaughn Vernon《实现领域驱动设计》(聚合与状态迁移)
  • Robert Martin《Clean Architecture》(策略与状态解耦的一般原则)