How Pulsar workflow types divide work
UseCases own application workflows; Operations reuse fragments. UseCases coordinate Actions, Operations, Queries, Contracts, transactions, and Events. Operations share branching fragments without becoming nested UseCases, while Actions stay atomic and Queries stay read-only.
Choose the workflow owner
UseCase vs Operation and Action vs Query
| Type | Owns and may call | Return | Must not |
|---|---|---|---|
| UseCase | One workflow; Actions, Operations, Queries, Events, and Domain Contracts. | Delivery-neutral value or void. | Call a UseCase or return HTTP/CLI delivery types. |
| Operation | Reusable multi-step branch for UseCases; Actions, Queries, Models, and Contracts. | Delivery-neutral value. | Be called by an adapter, call an Operation or UseCase, own a transaction or emit an Event. |
| Action | One atomic business operation over an aggregate; Model or Contract. | Domain value or void. | Call an Action or Query, coordinate Domains, own a transaction or Event. |
| Query | One complex read; Model. | Delivery-neutral read value. | Mutate or call a Query or Action. |
Every workflow-bearing type uses execute(). Stable dependencies belong in a constructor; runtime values belong in execute(). An HTTP Request and ambient Request or tenant state belong at the Adapter, not in a workflow constructor or signature.
Call graph and return boundary
The execute method carries runtime data
Interpretation. An HTTP Request reaches one Controller and then one read-only UseCase. That UseCase calls one or more Queries, returns a collection, DTO, or value without a transaction, and the Controller assembles a Resource or Inertia response.
The package's first feature proves the write route, not a Query route. Its absence is intentional: the generic figure preserves the current Controller → UseCase rule instead of manufacturing a fixture.
Put conditional work in Operations
Reuse one shared workflow fragment
Use an Operation only when multiple UseCases need the same multi-step sequence or conditional branch. The Operation remains a fragment inside its caller's workflow: it has no Adapter authority, transaction boundary, or Event-emission authority.
Context: ❌ Prohibited nested workflow — A reusable branch must not become a second UseCase.
$useCase->execute($data);Context: ✅ Correct reusable fragment — The UseCase delegates the shared branch to an Operation.
$operation->execute($data);Transform nested UseCases and Action composition
Replace a nested UseCase
An Action is not a workflow container. Keep its aggregate Operation atomic; make the UseCase or a reusable Operation select and order separate steps. Likewise, a UseCase never calls another UseCase: extract the shared fragment into an Operation rather than nesting transactions, Events, or application entrypoints.