ADR 0006: Workflow engine architecture with pluggable backends¶
Status¶
Accepted
Context¶
GeneralManager already has extension patterns for pluggable runtime components:
- Search backend registry (
SEARCH_BACKEND) with settings-driven resolution. - Audit logger registry (
AUDIT_LOGGER) with protocol-style contracts. - Startup hooks and system checks registered by capabilities.
Teams now need workflow orchestration for event-driven automation, including:
- Triggering workflows from package-level events.
- Executing reusable actions from workflow steps.
- Supporting both local execution and external orchestrators (for example n8n).
Introducing workflows should reuse existing package conventions instead of adding a one-off subsystem with different configuration and extension behavior.
Decision¶
Add a dedicated general_manager.workflow subsystem with three layers:
- Event ingress
WorkflowEventmodel andEventRegistryprotocol.- Default
InMemoryEventRegistrywith deduplication byevent_id. -
Registry receives domain events and routes them to handlers.
-
Engine abstraction
WorkflowEngineprotocol withstart,resume,cancel, andstatus.WorkflowDefinitionandWorkflowExecutionshared dataclasses.-
Settings-driven engine registry using
GENERAL_MANAGER["WORKFLOW_ENGINE"](or top-levelWORKFLOW_ENGINE) and import-path/callable/mapping resolution, consistent with search and audit configuration. -
Action execution
Actionprotocol andActionRegistryfor named side-effect operations.- Workflows invoke actions through the registry to centralize contracts, policy checks, retries, and observability in one place.
Backends:
- LocalWorkflowEngine: default in-memory backend for development and tests.
- CeleryWorkflowEngine: durable, database-backed backend for production workloads.
- N8nWorkflowEngine: adapter stub for future remote orchestration support.
Alternatives considered¶
1) Hard-couple workflow orchestration into interface capabilities - Rejected: workflows are cross-cutting and not specific to interface CRUD concerns; coupling would blur boundaries and complicate adoption.
2) Build only an n8n integration without local backend - Rejected: raises adoption barrier for tests/local development and creates lock-in before core contracts stabilize.
3) Reuse Django signals directly without an event registry contract - Rejected: signals are useful transport, but they do not provide clear event typing, deduplication, or backend-agnostic routing guarantees.
Consequences¶
- Establishes stable contracts before implementing full orchestration semantics.
- Preserves package consistency by reusing existing pluggable configuration style.
- Enables incremental adoption: local first, external orchestrators later.
- Keeps room for EventSourcing/CQRS work on the roadmap without forcing it now.
Follow-up work¶
- Define event schema validation conventions and replay tooling.
- Implement n8n adapter API calls and callback mapping.
- Add concept/how-to docs for event triggers, actions, and backend setup.
Implemented in this ADR rollout: - workflow engine configuration is wired during app startup. - durable execution persistence is provided by CeleryWorkflowEngine using Django models.