Extending Orchestra¶
Orchestra is built to be extended. This page describes the public extension
surface, the part marked @api and covered by the 1.x backward-compatibility
promise, and how to add your own behavior.
What is public¶
Classes and interfaces tagged @api form the supported contract. Within the
1.x line their method signatures will not change in a breaking way. Everything
else, in particular the concrete service implementations tagged @internal
(WorkflowEngine, WorkItemManager, OrchestraAudit, AttachmentManager),
is mechanics that may change between minor releases: depend on the matching
interface, not the implementation.
Plugin types¶
Each plugin type is a discovery directory, an attribute and a base class. To add
one, drop a class in your module's src/Plugin/<Type>/ directory, give it the
attribute, and extend the base class (or implement the interface directly).
| Plugin type | Directory | Attribute | Base class / interface |
|---|---|---|---|
| Task type | Plugin/TaskType |
#[TaskType] |
TaskTypeBase / TaskTypeInterface |
| Gateway | Plugin/Gateway |
#[Gateway] |
GatewayBase / GatewayInterface |
| Flow condition | Plugin/FlowCondition |
#[FlowCondition] |
FlowConditionBase / FlowConditionInterface |
| Split | Plugin/Split |
#[Split] |
SplitBase / SplitInterface |
| Join | Plugin/Join |
#[Join] |
JoinBase / JoinInterface |
| Timeout action | Plugin/TimeoutAction |
#[TimeoutAction] |
TimeoutActionBase / TimeoutActionInterface |
| Node feature | Plugin/NodeFeature |
#[NodeFeature] |
NodeFeatureBase / NodeFeatureInterface |
| Audience | Plugin/Audience |
#[Audience] |
AudienceBase / AudienceInterface |
An audience resolves who a node notifies (recipients()). An audience that can
also staff a user task additionally implements AssignmentInterface (extending
AudienceInterface with candidates()/viewerTokens()) and extends
AssignmentBase; a pure notification audience (a fixed external email) extends
AudienceBase alone and never appears in the task assignment editor.
A task type can opt into extra capabilities by implementing the matching
capability interface: SubprocessTaskInterface (launch a child process),
ParkingInterface (park the token and wait), PluginSequenceFeatureInterface
(compose node features), WorkItemPresentationInterface and
HandlerUrlInterface (inbox presentation and external handlers),
OrchestraContextAwareInterface (receive the running token and instance).
Service contracts¶
These interfaces are safe to type-hint, decorate or mock:
WorkflowEngineInterface(orchestra.engine): start instances, advance, signal, cancel and read or write process variables.WorkItemManagerInterface(orchestra_inbox.manager): create, claim, reassign and complete human tasks.TenantContextInterface(orchestra.tenant_context): resolve the active tenant.TenantResolverInterface: a service taggedorchestra.tenant_resolverthat decides the active tenant; return a tenant machine name orNULLto defer.OrchestraAuditInterface(orchestra.audit): emit lifecycle audit events.AttachmentManagerInterface(orchestra_content.attachment_manager): bind content entities to a running instance.
The entity contracts (ProcessInstanceInterface, TokenInterface,
VariableInterface, WorkflowInterface, TenantInterface,
WorkItemInterface, AttachmentInterface) are likewise part of the public
read API.
Events¶
The engine dispatches typed domain events you can subscribe to:
InstanceStartedEvent: an instance has been created and its variables seeded, but it has not run yet. It fires inside the start transaction, before the start token is placed, so a subscriber can wire an external entity to the instance (it has its id) atomically with the start, with the link in place before the run. A subscriber that throws rolls the whole start back, so keep them quick. (A plain entity insert hook is not enough: it fires before the variables are seeded, so a subscriber could not yet tell which entity the run is for.) The matching reverse link is the instance id, the integer primary key, which an external table can hold as a foreign key for a Views relationship.TaskTimedOutEvent: a parked task'snotifytimeout fired without resuming it; turn it into a reminder or alert. See Notifications.
Resolving the tenant¶
A multi-tenant site decides the active tenant through resolvers. Tag a service
orchestra.tenant_resolver, implement TenantResolverInterface, and return the
tenant machine name for the request, or NULL to let the next resolver decide.
The first non-null answer wins; with none the default tenant applies. The
orchestra_domain submodule ships a resolver that maps the current domain to a
tenant.