Workflow Engine 可脱离 ASP.NET Core 和 Platform 模块运行,但不是开箱即用的数据库产品。宿主必须提供 IPersistenceStore、事务、Schema、租户边界和表达式求值器;可选端口缺失时有明确降级。
Persistence 与 ExpressionEvaluator 是构建硬依赖;权限、查询、通知、参与人和服务任务虽然属于可选端口,却分别影响 fail-closed 写入、查询效率、消息送达、任务分配与自动节点。生产宿主必须把“可构建”和“可运营”分开验收。
1. 包边界
| 包 | 用途 |
|---|---|
| BitzOrcas.Workflow.Abstractions | 定义、运行模型和所有端口 |
| BitzOrcas.Workflow.Engine | 编译、节点行为、运行与服务实现 |
| BitzOrcas.Workflow.DependencyInjection | IServiceCollection 组合 |
| BitzOrcas.Workflow.FusionCache | 可选 L1/L2 缓存 |
| BitzOrcas Infrastructure adapters | EF Core/SqlSugar 写 Store 与 QueryStore |
不要让独立业务代码引用 Platform.Workflow.Application,除非也要采用 BitzOrcas 的 CurrentUser、Mediator、授权与 Endpoint。
2. 直接 Builder
var engine = new WorkflowEngineBuilder() // ① 必需:负责全部工作流表、事务和乐观并发。 .UsePersistence(persistenceStore) // ② 必需:内置安全表达式求值器。 .UseExpressionEvaluator(new DefaultExpressionEvaluator()) // ③ 用户交互生产环境强烈要求;缺失时 fail-closed。 .UseFlowDataPermissionChecker(permissionChecker) .Build();
IWorkflowTransitionFlow transitions = engine.RuntimeTransition;ITaskService tasks = engine.Tasks;Builder 若缺少 Persistence 或 ExpressionEvaluator 会抛 InvalidOperationException。Noop cache、无 QueryStore、无通知、无参与人 Provider 都允许构建,但能力会降级。
3. DI 组合
// 写 Store 与读 Store 分离注册,二者必须共享租户语义。services.AddScoped<IPersistenceStore, TenantAwareWorkflowStore>();services.AddScoped<IWorkflowQueryStore, WorkflowReadStore>();services.AddScoped<IFlowDataPermissionChecker, WorkflowPermissionChecker>();services.AddScoped<IParticipantProvider, DirectoryParticipantProvider>();services.AddScoped<INotificationChannel, DurableWorkflowNotificationChannel>();services.AddScoped<IServiceTaskHandler, ApplicationServiceTaskHandler>();
// AddWorkflowEngine 负责引擎服务图,不会替宿主创建 Schema。services.AddWorkflowEngine(options =>{ options.Configuration = new WorkflowEngineConfiguration();});AddWorkflowEngine 注册 IWorkflowEngine 为 Scoped,避免捕获 Scoped DbContext。它会使用默认表达式求值器,并把聚焦端口分别转发到引擎。
注意:WorkflowEngineConfiguration 属性当前没有运行时消费;不要依赖 EnableHistory=false 等开关。
4. IPersistenceStore 合同
Store 不只是 CRUD。它必须保证:
- BeginTransaction 覆盖同一操作的多表写;
- Instance/Task 更新使用 expectedVersion;
- Tenant filter 对所有读写一致;
- Definition、Deployment、Instance、Execution、Task、Candidate、Activity、Timer、History、Statistics 的语义完整;
- 取消令牌传递;
- 唯一冲突和并发异常映射到 Engine 可识别类型;
- 时间使用 UTC DateTimeOffset;
- 查询排序稳定。
只实现“让接口编译”会产生静默错误。优先复用官方 Adapter 并运行 parity tests。
5. QueryStore
没有 IWorkflowQueryStore:
- 简单待办可回退 IPersistenceStore;
- 已办总数可能全量扫描;
- 报表多返回空;
- 统计预聚合没有原始数据。
生产宿主应实现数据库端 Assigned/Candidate UNION、租户谓词、count/page、报表范围查询。DataScope 高级过滤当前仍需框架改造,不能由宿主文档假定已下推。
6. 参与人和权限
IParticipantProvider 展开角色/岗位/组织;IParticipantResolver 处理自定义规则。两者都必须限定当前租户,返回稳定去重 UserId。
IFlowDataPermissionChecker 是运行红线。View、Operate、Manage 应分别映射到宿主授权系统,且 userId/tenantId 必须来自可信调用上下文。缺失 checker 时任务详情和写操作 fail-closed,这是安全默认值。
7. ServiceTask
ServiceTaskBehavior 调用 IServiceTaskHandler。Implementation 是 DSL 字符串,宿主应把它映射到 allowlist handler key,不执行任意反射或脚本。
public Task HandleAsync( string implementation, IExecutionContext context, CancellationToken cancellationToken){ // implementation 只能映射到受控白名单,不能反射执行任意类型。 return implementation switch { "matter.reserve-case-number" => reserveCaseNumber.ExecuteAsync(context, cancellationToken), "conflict.register-waiver" => registerWaiver.ExecuteAsync(context, cancellationToken), // 未知 key 必须明确失败并进入运维告警。 _ => throw new InvalidOperationException( $"Unsupported service task: {implementation}") };}外部调用必须有幂等键、超时、重试和补偿;不要把数据库事务跨网络保持。
8. 通知和业务回调
INotificationChannel 可选;缺失时流程不发通知。自建 Channel 应对齐平台适配器的契约——逐接收人持久化站内信与 Outbox 事实,任一失败计数后抛出异常——因为引擎侧的分发器现在会把异常向上重抛、令流转失败回滚,半途吞掉异常等于伪造成功。直接 Builder 可以接模板与偏好 Store,DI 扩展当前不会自动转发这两个端口。
IBusinessIntegrationCallback 失败语义要特别设计,因为它不是 Outbox。建议回调只写本地可靠 intent,异步更新业务系统。
9. Timer
IJobScheduler 负责创建调度记录;IWorkflowTimerProcessor/TimerJobHandler 负责扫描。独立宿主需要:
- 每个 Job 的可信租户作用域;
- 原子 Claim/lease;
- attempt、backoff、DLQ;
- 多实例互斥;
- Reminder 的通知渠道;
- Escalation/Transfer 的系统权限;
- crash/restart 演练。
官方当前 Timer 先 Fired 后动作,嵌入方不能直接宣称可靠超时。
10. 缓存
默认 Noop 最简单且正确。引入缓存后,定义、部署和待办计数失效成为业务正确性的一部分。FusionCache 无 Redis 时只是进程内 L1;多节点需要 distributed cache + backplane,并验证故障降级。
11. 健康与观测
Readiness 检查数据库/Schema、QueryStore、租户上下文和必要端口;Timer/通知可作为独立组件状态。Metrics 覆盖命令延迟、冲突、任务年龄、Timer lag、通知 intent backlog、报表扫描与缓存。
日志包含 definitionId/instanceId/taskId 时要按敏感标识治理;Variables、Comment、BusinessKey 默认不得完整输出。
12. 独立宿主验收
- 官方 Workflow unit 与 contract tests 通过;
- 自定义 Store parity tests 覆盖事务、并发、租户和所有表;
- 无 QueryStore 的降级被显式阻止或记录;
- 自定义参与人/权限有跨租户拒绝测试;
- ServiceTask 重放安全;
- Timer crash window 与重试可恢复;
- 通知与业务回调有持久 intent;
- Schema migration、备份、恢复、归档和清理 Runbook 可执行。
13. 验证命令
dotnet test tests/BitzOrcas.Workflow.Tests/BitzOrcas.Workflow.Tests.csprojdotnet test tests/BitzOrcas.Workflow.Integration.Tests/BitzOrcas.Workflow.Integration.Tests.csproj
rg -n "class .*PersistenceStore|IWorkflowQueryStore" src/Framework tests -g '*.cs' --glob '!**/bin/**' --glob '!**/obj/**'