一佳技术

状态机(FSM)与 DDD 框架集成技术方案

目录

  1. 背景与目标
  2. 现状分析
  3. 整体架构设计
  4. 核心模块设计
  5. 与 DDD 框架的集成方案
  6. 实施计划
  7. 风险与应对

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/状态机注册中心
StatefulDomainServicekernel-domain/stateful/带状态机的领域服务基类
FsmBuilder 增强easy-flows/fsm/增加 Action 支持
FsmAutoConfigurationkernel-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:核心类文件索引

类/文件路径
FsmFsm.java
FsmBuilderFsmBuilder.java
TransitTransit.java
TransitsTransits.java
Stateful<S>Stateful.java
AggregateRootAggregateRoot.java
BaseDomainServiceBaseDomainService.java
StatefulDomainServiceStatefulDomainService.java
FsmRegistryFsmRegistry.java
DefaultFsmRegistryDefaultFsmRegistry.java
FsmAutoConfigurationFsmAutoConfiguration.java

附录 B:关键决策记录

决策选项结论理由
包名fsm vs statefulstateful命名更准确,与 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-domainkernel-domaineasy-flows 是通用组件,DDD 集成属于框架层