Тема
WorkflowStore
WorkflowStore — MobX-store состояния составной операции. Создание:
ts
new WorkflowStore(options)Factory-функции нет.
Пример: bootstrap кабинета
Профиль и permissions независимы и загружаются параллельно. Dashboard settings можно запросить только после permissions.
ts
import { makeAutoObservable } from "mobx"
import { WorkflowStore } from "@dubium/query-layer/orchestration"
import type { PermissionsStore } from "../../permissions/model"
import type { ProfileStore } from "../../profile/model"
import type { SettingsStore } from "../../settings/model"
export class DashboardBootstrapStore {
// WorkflowStore координирует domain stores.
private readonly workflow: WorkflowStore
constructor(profileStore: ProfileStore, permissionsStore: PermissionsStore, settingsStore: SettingsStore) {
this.workflow = new WorkflowStore({
// На одном execution level не больше двух параллельных nodes.
concurrency: 2,
nodes: [
{
id: "profile",
// ProfileStore внутри использует RequestStore/FetchStore.
execute: async () => profileStore.fetch(),
},
{
id: "permissions",
execute: async () => permissionsStore.fetch(),
},
{
id: "settings",
// Этот node стартует только после permissions.
dependencies: ["permissions"],
// results содержит значения completed dependencies.
execute: async ({ results }) => {
const permissions = results.get("permissions") as string[]
return settingsStore.fetch({
includeAdmin: permissions.includes("admin"),
})
},
},
],
})
makeAutoObservable<this, "workflow">(
this,
{
// Вложенный WorkflowStore уже observable.
workflow: false,
},
{
autoBind: true,
},
)
}
get running(): boolean {
return this.workflow.status === "running"
}
get status(): string {
return this.workflow.status
}
get completedSteps(): number {
return this.workflow.progress?.completed ?? 0
}
get totalSteps(): number {
return this.workflow.progress?.total ?? 3
}
get error(): unknown {
return this.workflow.error
}
async initialize(): Promise<void> {
// Каждый execute отменяет предыдущий активный запуск этого WorkflowStore.
await this.workflow.execute()
}
cancel(): void {
this.workflow.cancel()
}
dispose(): void {
this.workflow.dispose()
}
}Конфигурация
| Option | Type | Default | Что делает |
|---|---|---|---|
nodes | readonly IWorkflowNode[] | — | Обязательный список шагов. |
concurrency | number | без ограничения уровня | Максимальное число nodes одного level, выполняемых одновременно. Минимум 1. |
persister | IWorkflowPersister | — | Adapter checkpoint storage. |
workflowId | string | — | Стабильный id checkpoint. Обязателен вместе с persister. |
now | () => number | Date.now | Источник времени snapshot, обычно для тестов. |
Состояние
| Поле | Type | Что означает |
|---|---|---|
status | idle | running | success | error | cancelled | Состояние последнего запуска. |
progress | WorkflowProgressEvent | null | Последнее событие конкретного node. |
results | ReadonlyMap<string, unknown> | Результаты последнего успешного workflow. |
error | unknown | Ошибка последнего неуспешного запуска. |
Во время нового запуска error очищается, но results предыдущего успеха остаются до нового success. Если UI не должен показывать старые данные, domain store должен учитывать status === "running".
Методы
| Метод | Возвращает | Что делает |
|---|---|---|
execute() | Promise<ReadonlyMap<string, unknown>> | Отменяет предыдущий запуск и выполняет workflow. |
cancel() | void | Abort текущего workflow. Уже завершённые nodes могут быть компенсированы. |
dispose() | void | Отменяет текущий запуск и освобождает controller. |
У WorkflowStore нет reset(). Если нужен новый независимый lifecycle, создайте новый instance или скройте отображение старого результата в domain getters.
Progress event
ts
interface WorkflowProgressEvent {
nodeId: string
status: "running" | "success" | "error" | "skipped"
completed: number
total: number
}progress хранит последнее событие, а не массив всей истории. Для audit log собирайте события на уровне приложения или logger.
Следующая страница: узлы и зависимости.