状态机工具(Aegis.Toolkit.StateMachine)
Aegis.Toolkit.StateMachine 用来把复杂状态流转从业务代码里拆出来。它提供状态上下文、状态验证器、状态处理器和状态机基类,适合把“哪些状态能转、进入某状态要做什么、退出某状态要做什么”统一收口。
组件概览
| 字段 | 说明 |
|---|---|
| 组件名称 | 状态机工具 |
| 真实类库 | Aegis.Toolkit.StateMachine |
| 组件定位 | 通用状态机工具包 |
| 引入方式 | 安装 NuGet,并在 Component.deps.json 的 Services 中启用 Toolkit.StateMachine |
| 组件声明 | Toolkit.StateMachine |
| 核心能力 | StateContext<TState>、StateValidator<TState>、StateHandlerManager<TState, TContext>、BaseStateMachine<TState, TContext> |
| 典型配套 | Services、Repository |
什么时候要用它
适合场景:
- 业务存在明确的状态流转图
- 状态切换前后有校验和副作用
- 你希望把状态流转规则从 Service 里拆出来
最小可运行路径
第一步:启用 Toolkit.StateMachine
{
"Components": {
"Services": [
"Toolkit.StateMachine"
],
"Middlewares": []
}
}
第二步:定义状态枚举
public enum OrderState
{
Pending,
Processing,
Completed,
Cancelled
}
第三步:定义状态上下文
public class OrderContext : StateContext<OrderState>
{
public int OrderId { get; set; }
}
第四步:定义状态处理器
public class ProcessingHandler : IStateHandler<OrderState, OrderContext>
{
public OrderState State => OrderState.Processing;
public ApplyStateResult CanTransition(OrderContext context, OrderState? newState)
{
return new ApplyStateResult
{
IsSuccess = true,
Code = 200,
Message = "OK"
};
}
public void Enter(OrderContext context, OrderState? oldState)
{
}
public void Exit(OrderContext context)
{
}
}
第五步:定义状态机
public class OrderStateMachine : BaseStateMachine<OrderState, OrderContext>
{
public OrderStateMachine(
StateValidator<OrderState> validator,
StateHandlerManager<OrderState, OrderContext> stateHandlerManager)
: base(validator, stateHandlerManager)
{
}
protected override Task<bool> PersistStateAsync(OrderContext context)
{
return Task.FromResult(true);
}
}
第六步:应用状态变更
var transitions = new Dictionary<OrderState, OrderState[]>
{
{ OrderState.Pending, new[] { OrderState.Processing, OrderState.Cancelled } },
{ OrderState.Processing, new[] { OrderState.Completed } }
};
var validator = new StateValidator<OrderState>(
transitions,
new[] { OrderState.Pending });
var context = new OrderContext
{
CurrentState = OrderState.Pending,
OrderId = 1001
};
await stateMachine.ApplyStateAsync(context, OrderState.Processing);
这组类型分别负责什么
| 类型 | 负责什么 |
|---|---|
StateContext<TState> | 携带当前状态和上下文数据 |
StateValidator<TState> | 负责状态流转规则校验 |
IStateHandler<TState, TContext> | 负责某个状态的进入、退出和转移检查 |
StateHandlerManager<TState, TContext> | 收集和分发状态处理器 |
BaseStateMachine<TState, TContext> | 统一执行“校验 -> 退出 -> 持久化 -> 进入”流程 |
状态机实际执行流程
ApplyStateAsync(...) 的主流程可以理解成:
- 先检查当前状态能不能转到目标状态
- 旧状态处理器先执行
Exit(...) - 旧状态处理器再做
CanTransition(...) - 更新
CurrentState - 调用
PersistStateAsync(...)持久化 - 新状态处理器执行
Enter(...)
这意味着:
真正的数据库保存逻辑,应该写在你自己的 PersistStateAsync(...) 里。
边界与限制
当前接口签名是双泛型
业务处理器应实现的是:
IStateHandler<TState, TContext>
而不是旧写法里的单泛型版本。
状态机本身不替你做持久化
BaseStateMachine 只约束流程,不直接落库。
状态保存仍然由你自己的业务状态机实现。
更推荐在 Service 层组织状态机调用
状态机负责“状态怎么变”,Service 负责“什么时候变、为什么变、和哪些业务动作一起变”。
接入后怎么确认已经生效
可以这样确认:
Toolkit.StateMachine启用后没有装配异常- 调用
ApplyStateAsync(...)时,非法状态能被挡住 - 合法状态能更新并进入对应处理器
常见问题
为什么状态机里还要自己写 PersistStateAsync(...)
因为当前工具包只提供统一流程,不替业务决定如何保存状态。
为什么处理器没有被自动发现
当前更稳的口径是:把状态机相关类型作为业务内显式组织的工具来用,不要依赖模糊的自动发现预期。
状态机应该写在 Controller 吗
不建议。
更推荐放在 Services 或领域层里。