状态机(FSM)与 DDD 框架集成技术方案
目录
1. 背景与目标
1.1 背景
Matrix Framework 已具备:
- 完整的 DDD 分层架构(Adapter / Application / Domain / Infrastructure)
- 基于 Avro IDL 的领域建模 DSL
- 基于 COLA StateMachine 封装的状态机组件(
easy-flows模块)
但当前状态机与 DDD 框架之间存在断层:
- 领域服务中状态校验依赖硬编码和程序员自觉,缺乏机制化保障
- 聚合根的状态字段与状态机没有自动关联
1.2 目标
将状态机能力无缝融入 DDD 框架,实现:
| 目标 | 说明 |
|---|---|
| 代码式状态流转 | 通过 FsmBuilder 以代码方式定义状态机,子类实现 buildFsm() 方法 |
| 自动化状态校验 | 领域方法执行前调用 checkTransition() 校验当前状态是否允许执行目标操作 |
| 可视化状态图 | 自动生成全状态迁移图(PlantUML),支持业务评审和前端展示 |
| 可查询可执行 | 支持查询聚合根在当前状态下可执行的操作列表 |
| 零侵入集成 | 不破坏现有 DDD 框架结构,以增强方式提供能力 |
2. 现状分析
2.1 现有状态机能力(easy-flows 模块)
基于 COLA StateMachine 二次封装,核心类位于 components/easy-flows/src/main/java/io/matrix/flows/fsm/:
flowchart LR
subgraph "easy-flows fsm 模块"
Fsm["Fsm<br/>状态机核心<br/>· fireEvent 触发转换<br/>· readyEvent 判断可执行<br/>· checkReadyEvent 校验<br/>· readyEvents 获取可执行列表<br/>· generatePlantUML 生成状态图"]
FsmBuilder["FsmBuilder<br/>状态机构建器<br/>· external 外部转换<br/>· internal 内部转换<br/>· condition 条件判断<br/>· perform 动作回调"]
Transit["Transit<br/>单次转换模型<br/>from / to / event / metadata"]
Transits["Transits<br/>转换集合<br/>· isReady4Event<br/>· getEvents / getFromStates"]
ReadyEventVisitor["ReadyEventVisitor<br/>计算可执行事件的访问者"]
PlantUMLVisitor["PlantUMLVisitor<br/>生成 PlantUML 状态图"]
end
FsmBuilder -->|构建| Fsm
Fsm -->|使用| ReadyEventVisitor
Fsm -->|使用| PlantUMLVisitor
ReadyEventVisitor -->|输出| Transits
Transits -->|包含| Transit
已覆盖的核心能力:
- ✅ 构建全状态迁移图(
generatePlantUML()) - ✅ 根据当前状态判断可以执行操作(
readyEvents(S, C)) - ✅ 检验当前状态是否可执行某操作(
readyEvent()/checkReadyEvent())
2.2 现有 DDD 框架结构
flowchart LR
subgraph "kernel-domain 模块"
AggregateRoot["AggregateRoot 接口<br/>· identifier()"]
Stateful["Stateful<S> 接口<br/>· getStatus()<br/>· setStatus(S)"]
AggregateBase["AggregateBase 抽象基类"]
BaseRepository["BaseRepository 接口"]
BaseService["BaseService<br/>· publish() 事件发布<br/>· CommandBus 命令总线"]
BaseDomainService["BaseDomainService<br/>· 持有 repository"]
StatefulDomainService["StatefulDomainService<br/>· 状态机校验/触发/查询"]
BaseAppService["BaseAppService<br/>应用服务基类"]
end
AggregateBase -.-> AggregateRoot
BaseDomainService --> BaseService
StatefulDomainService --> BaseDomainService
BaseAppService --> BaseService
BaseDomainService --> BaseRepository
关键改进:
- 新增
Stateful<S>接口,聚合根实现后可直接获取/设置状态 - 新增
StatefulDomainService,提供状态机校验、转换、查询能力
2.3 已实现的状态机功能
| 功能 | 状态 | 说明 |
|---|---|---|
| Stateful | ✅ 已实现 | 聚合根实现此接口,提供类型安全的状态访问 |
| FsmRegistry 注册中心 | ✅ 已实现 | 统一管理所有状态机实例,支持按聚合根类型或名称注册/获取 |
| StatefulDomainService | ✅ 已实现 | 领域服务基类,封装状态机常用操作 |
| 状态转换动作(Action) | ✅ 已实现 | FsmBuilder.perform(action) 支持转换回调 |
| 状态校验 | ✅ 已实现 | checkTransition() 不允许则抛异常 |
| 状态转换 | ✅ 已实现 | fireTransition() 执行转换并自动更新聚合根状态 |
| 可执行操作查询 | ✅ 已实现 | listAvailableTransitions() 查询当前可执行的转换列表 |
| 状态图生成 | ✅ 已实现 | generatePlantUML() 生成 PlantUML 状态图 |
| 自动配置 | ✅ 已实现 | FsmAutoConfiguration 自动注册 FsmRegistry |
| 状态变更历史/审计 | ❌ 待实现 | 谁、何时、因什么事件、从A到B,没有自动记录 |
| 事务边界内的状态转换 | ❌ 待实现 | fireEvent 与领域操作的事务原子性无保障 |
| 多状态字段支持 | ❌ 待实现 | 一个聚合根可能有多个独立状态机 |
3. 整体架构设计
3.1 分层架构图
flowchart LR
subgraph "定义层(FsmDefinition)"
B1["FsmDefinition<br/>状态机完整定义<br/>· 聚合根类型<br/>· 状态字段<br/>· 状态枚举/事件枚举<br/>· 初始状态<br/>· 转换规则列表"]
B2["TransitionDef<br/>单条转换定义<br/>· fromStates 源状态列表<br/>· toState 目标状态<br/>· event 触发事件<br/>· condition 条件<br/>· action 动作"]
end
subgraph "注册层(FsmRegistry)"
C1["FsmRegistry<br/>状态机注册中心<br/>· 按聚合根类型注册<br/>· 按名称注册"]
end
subgraph "构建层(代码方式)"
G1["子类实现 buildFsm()<br/>使用 FsmBuilder 构建状态机"]
end
subgraph "运行层(状态机执行)"
D1["StatefulDomainService<br/>带状态机的领域服务基类<br/>· getFsm() 获取状态机<br/>· checkTransition() 校验转换<br/>· fireTransition() 执行转换<br/>· listAvailableTransitions() 查询可执行操作"]
D2["Fsm<br/>已有状态机核心(复用)"]
end
subgraph "领域层(DDD)"
E1["AggregateRoot & Stateful<S><br/>聚合根(含状态字段)"]
E2["BaseDomainService<br/>已有领域服务基类(复用)"]
E3["Repository<br/>仓储"]
end
G1 -->|构建| D2
B1 -->|包含| B2
D2 -->|注册到| C1
D1 -->|继承| E2
D1 -->|获取| C1
D1 -->|操作| E1
E1 -->|持久化| E3
3.2 核心流程
3.2.1 状态机构建流程
flowchart LR
Start["开始"] --> Subclass["子类继承 StatefulDomainService"]
Subclass --> BuildFsm["实现 buildFsm() 方法<br/>使用 FsmBuilder 构建 Fsm"]
BuildFsm --> InitFsm["@PostConstruct 自动调用 initFsm()"]
InitFsm --> Register["注册到 FsmRegistry"]
Register --> Done["完成"]
3.2.2 领域操作中的状态机使用流程
flowchart LR
Cmd["收到 Command"] --> Load["加载聚合根"]
Load --> Check["checkTransition(aggregate, event)<br/>校验当前状态是否允许执行目标事件"]
Check -->|校验失败| Err["抛出 IllegalStateException"]
Check -->|校验通过| Business["执行业务逻辑"]
Business --> Fire["fireTransition(aggregate, event)<br/>触发状态转换,自动更新聚合根状态字段"]
Fire --> Save["持久化聚合根"]
Save --> Publish["发布领域事件"]
Publish --> End["返回结果"]
4. 核心模块设计
4.1 模块划分
flowchart LR
subgraph "easy-flows(已有,不改动)"
FsmMod["fsm/ 包<br/>Fsm, FsmBuilder, Transit, Transits, Visitor"]
end
subgraph "kernel-domain(新增 stateful 包)"
StatefulItf["Stateful<S> 接口<br/>聚合根状态访问"]
Reg["FsmRegistry 接口<br/>+ DefaultFsmRegistry 默认实现"]
Def["FsmDefinition 模型<br/>FsmDefinition, TransitionDef"]
Service["StatefulDomainService 基类<br/>带状态机的领域服务"]
Auto["FsmAutoConfiguration<br/>Spring Boot 自动配置"]
end
subgraph "业务模块(如 pulse-domain)"
BizAggregate["Requirement<br/>implements AggregateRoot, Stateful<Status>"]
BizService["RequirementDomainService<br/>extends StatefulDomainService"]
end
kernel-domain --> easy-flows
BizAggregate --> kernel-domain
BizService --> kernel-domain
4.2 核心类设计
4.2.1 Stateful — 有状态的聚合根接口
public interface Stateful<S> {
S getStatus();
void setStatus(S status);
}
聚合根实现此接口,提供类型安全的状态访问。配合 Lombok @Data 使用时零额外代码(自动生成 getter/setter)。
4.2.2 FsmRegistry — 状态机注册中心
接口: FsmRegistry
├── get(Class<T> aggregateClass) → Fsm<S,E,T>
│ 按聚合根类型获取状态机
├── get(String fsmName) → Fsm<S,E,T>
│ 按名称获取状态机
├── register(Class<T> aggregateClass, Fsm fsm) → void
│ 按聚合根类型注册
└── register(String fsmName, Fsm fsm) → void
按名称注册
默认实现: DefaultFsmRegistry
└── ConcurrentHashMap 缓存
4.2.3 StatefulDomainService — 领域服务状态机增强
继承关系:
BaseService
├── BaseDomainService(已有,不改动)
│ └── StatefulDomainService(新增)
└── BaseAppService
泛型参数(5个):
StatefulDomainService<
T extends AggregateRoot & Stateful<S>, // 聚合根类型
R extends BaseRepository<T>, // 仓储类型
S extends Enum<S> & NamedEntry, // 状态枚举类型
E extends Enum<E> & NamedEntry, // 事件枚举类型
C // 上下文类型,用于条件判断和动作回调
>
核心方法:
| 方法 | 说明 |
|---|---|
getFsmName() | (抽象)返回状态机名称 |
buildFsm() | (抽象)使用 FsmBuilder 构建状态机 |
getFsm() | 从 FsmRegistry 获取当前聚合根对应的状态机 |
extractState(aggregate) | 调用 aggregate.getStatus() 提取当前状态 |
setState(aggregate, newState) | 调用 aggregate.setStatus() 设置状态 |
checkTransition(aggregate, event, ctx) | 校验是否可执行,不可执行抛 IllegalStateException |
fireTransition(aggregate, event, ctx) | 执行状态转换并自动更新聚合根状态字段,返回新状态 |
listAvailableTransitions(aggregate, ctx) | 查询当前可执行的转换列表 |
4.3 FsmBuilder 增强 — Action 支持
在现有 FsmBuilder.external() 方法基础上增加:
.external(S from, S to, E event)
.perform((from, to, event, ctx) -> {
// 转换发生时的回调动作
})
5. 与 DDD 框架的集成方案
5.1 聚合根接入
聚合根实现 AggregateRoot 和 Stateful<S> 接口:
@Data
@DomainObject(...)
public class Requirement implements AggregateRoot, Stateful<RequirementStatus> {
private RequirementStatus status; // Lombok @Data 自动生成 getStatus()/setStatus()
// ... 其他字段
}
5.2 领域服务接入
领域服务继承 StatefulDomainService,实现两个抽象方法:
@Service
@RequiredArgsConstructor
@Slf4j
public class RequirementDomainService extends StatefulDomainService<
Requirement, RequirementRepository, RequirementStatus, RequirementEvent, Requirement> {
@Override
protected String getFsmName() {
return "RequirementFsm";
}
@Override
protected Fsm<RequirementStatus, RequirementEvent, Requirement> buildFsm() {
return FsmBuilder
.builder(RequirementStatus.class, RequirementEvent.class,
Requirement.class, getFsmName())
.external(RequirementStatus.PENDING_ASSESS, RequirementStatus.ASSESSED,
RequirementEvent.ASSESS)
.external(RequirementStatus.ASSESSED, RequirementStatus.ADOPTED,
RequirementEvent.ADOPT)
// ... 更多转换规则
.build();
}
// 业务方法
public void assessRequirement(AssessRequirementCmd cmd) {
Requirement requirement = repository.load(cmd.getRequirementCode());
// 状态校验:不允许则抛出 IllegalStateException
checkTransition(requirement, RequirementEvent.ASSESS, requirement);
// 执行业务逻辑...
// 触发状态转换:自动更新 requirement.status
fireTransition(requirement, RequirementEvent.ASSESS, requirement);
repository.save(requirement);
}
}
5.3 可执行操作查询示例
前端/API 层查询某聚合根当前可执行的操作:
// 在 ApplicationService 或 Adapter 层
public List<String> listAvailableActions(String requirementCode) {
Requirement requirement = repository.load(requirementCode);
Transits<RequirementStatus, RequirementEvent> transits =
requirementDomainService.listAvailableTransitions(requirement, requirement);
return transits.getEvents().stream()
.map(NamedEntry::getCode)
.collect(Collectors.toList());
}
5.4 状态图生成示例
// 生成 PlantUML 供文档或前端展示
String plantUml = requirementDomainService.getFsm().generatePlantUML();
6. 实施计划
6.1 实施进度
flowchart LR
subgraph "Phase 1:基础框架搭建(✅ 已完成)"
P1_1["Stateful<S> 接口"]
P1_2["FsmRegistry 注册中心"]
P1_3["StatefulDomainService 基类(4个泛型)"]
P1_4["FsmBuilder Action 支持"]
P1_5["FsmAutoConfiguration 自动配置"]
P1_6["集成测试 15/15 通过"]
end
subgraph "Phase 2:业务集成(✅ 已完成)"
P2_1["pulse 项目 Requirement 接入"]
P2_2["RequirementDomainService 重构"]
end
subgraph "Phase 3:增强能力(❌ 待实现)"
P3_1["状态变更历史自动记录(审计)"]
P3_2["多状态字段支持"]
P3_3["事务绑定(状态转换失败回滚)"]
P3_4["状态机 Graph 可视化 API"]
end
P1_1 & P1_2 & P1_3 & P1_4 & P1_5 --> P1_6
P1_6 --> P2_1 & P2_2
P2_2 --> P3_1 & P3_2 & P3_3 & P3_4
6.2 各阶段交付物
Phase 1(✅ 已完成)
| 交付物 | 位置 | 说明 |
|---|---|---|
Stateful<S> 接口 | kernel-domain/domain/ | 聚合根状态访问接口 |
FsmRegistry 接口 + 默认实现 | kernel-domain/stateful/ | 状态机注册中心 |
StatefulDomainService | kernel-domain/stateful/ | 带状态机的领域服务基类 |
FsmBuilder 增强 | easy-flows/fsm/ | 增加 Action 支持 |
FsmAutoConfiguration | kernel-domain/stateful/autoconfigure/ | Spring Boot 自动配置 |
| 集成测试 | kernel-domain/test/stateful/ | 15/15 通过 |
Phase 2(✅ 已完成)
| 交付物 | 位置 | 说明 |
|---|---|---|
| Requirement 聚合根接入 | pulse 项目 | 实现 Stateful<RequirementStatus> |
| RequirementDomainService 重构 | pulse 项目 | 继承 StatefulDomainService |
Phase 3(按需,待实现)
| 交付物 | 说明 |
|---|---|
| 状态审计日志 | AOP 拦截 fireTransition 自动记录 |
| 多状态字段 | FsmRegistry 支持按字段名区分 |
| 事务绑定 | @Transactional 与状态转换联动 |
| 可视化 API | 提供 Mermaid/PlantUML 输出接口 |
7. 风险与应对
| 风险 | 影响 | 概率 | 应对措施 |
|---|---|---|---|
| COLA StateMachine 升级兼容性 | 中 | 低 | 封装层做好隔离,不直接暴露 COLA API 给业务层 |
| 与现有业务代码的兼容 | 高 | 低 | StatefulDomainService 继承自 BaseDomainService,完全兼容;老代码可逐步迁移 |
| 状态转换与业务事务一致性 | 中 | 中 | Phase 3 实现事务绑定;当前依赖调用方保证事务边界 |
| 多状态字段复杂性 | 中 | 低 | 当前只支持单状态字段,Phase 3 再扩展 |
附录 A:核心类文件索引
| 类/文件 | 路径 |
|---|---|
| Fsm | Fsm.java |
| FsmBuilder | FsmBuilder.java |
| Transit | Transit.java |
| Transits | Transits.java |
| Stateful<S> | Stateful.java |
| AggregateRoot | AggregateRoot.java |
| BaseDomainService | BaseDomainService.java |
| StatefulDomainService | StatefulDomainService.java |
| FsmRegistry | FsmRegistry.java |
| DefaultFsmRegistry | DefaultFsmRegistry.java |
| FsmAutoConfiguration | FsmAutoConfiguration.java |
附录 B:关键决策记录
| 决策 | 选项 | 结论 | 理由 |
|---|---|---|---|
| 包名 | fsm vs stateful | stateful | 命名更准确,与 Stateful 接口、StatefulDomainService 对齐 |
| 状态访问方式 | 反射 vs 接口 | Stateful | 类型安全、零反射开销、编译期检查 |
| 泛型参数数量 | 5个(T,R,S,E,C) vs 4个(T,R,S,E) | 5个 | 上下文 C 用于条件判断和动作回调,支持复杂场景 |
| 领域服务继承 | 组合 vs 继承 | 继承 StatefulDomainService | 与现有 BaseDomainService 模式一致,侵入性低 |
| 状态机构建方式 | 注解驱动 vs 代码构建 | 代码构建(FsmBuilder) | 注解方式无法灵活表达条件判断,代码构建更灵活可维护 |
| 状态机放置位置 | easy-flows vs kernel-domain | kernel-domain | easy-flows 是通用组件,DDD 集成属于框架层 |