adachsoft / workflow
Framework-agnostic workflow engine and Public API with SPI for external steps.
Installs: 6
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/adachsoft/workflow
Requires
- php: ^8.2
- adachsoft/collection: ^2.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.87
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.3
This package is not auto-updated.
Last update: 2025-10-23 06:28:25 UTC
README
New in v0.6.1
- Breaking: InMemoryDomainStepRegistry no longer exposes registerType(). Use registerStep(StepInterface) which infers type from $step->getType().
- Docs & Examples: InlineScriptStep type is "script" (not "inline_script").
- README and examples updated accordingly.
New in v0.6.0
- Public API: listStepTypes() returns StepTypeCollection aggregated from Domain and SPI (no duplicates).
- New DTOs/collections: StepTypeDto and StepTypeCollection (immutable).
- SPI registration infers type from step->getType(); duplicate registration throws DuplicateStepTypeException.
- Canonical core types: script, menu_step, no_op, call_workflow (if registered in the domain registry).
New in v0.5.0
- Added CallWorkflowStep: run subworkflow from a step and await completion using RETRY. On completion, child's output becomes next step's input by default (1:1).
- See tests/Workflow/Fixtures/workflows/parent_subworkflow_demo.json and child_subworkflow_demo.json for JSON examples.
Workflow SPI for Steps — Public SPI for External Steps
This repository exposes a stable, framework-agnostic Service Provider Interface (SPI) that allows external libraries (providers/plugins) to implement workflow steps without depending on the Domain layer. The application hosts can register steps either manually or via automatic discovery using composer.json extra
configuration.
- Decoupled from Domain: providers implement only contracts from
AdachSoft\Workflow\Spi\Step\…
. - Two registration modes:
- Manual in the host application.
- Automatic via discovery of providers listed in
composer.json
.
- Domain engine consumes steps through adapters in
Application\PluginBridge
.
Requirements
- PHP >= 8.2
- adachsoft/collection >= 2.1.0
Concepts at a Glance (SPI Step)
StepInterface
: the SPI step to be executed by the engine.StepContextDto
: immutable input context for step execution (runId
,workflowDefinitionId
,stepId
,input
,sharedState
).StepResultDto
: the result of step execution (status
, optionalnextStepId
,output
).StepResultStatusEnum
: possible execution statuses:SUCCESS
,RETRY
,FAILURE
,SKIPPED
.StepFactoryInterface
: factory for creating step instances.StepProviderInterface
: provides multiple step registrations shipped by a provider package.StepRegistrationDto
: a single registration entry (factory + metadataTagCollection
).Tag
/TagCollection
: simple metadata attached to registration entries.
Host-side bridge and discovery:
Application\PluginBridge\StepRegistrationService
: stores registrations and exposes factories by type. Duplicate types throwDuplicateStepTypeException
.Application\PluginBridge\StepRegistryPortAdapter
: implements DomainStepRegistryPortInterface
on top of SPI registrations.Application\PluginBridge\DomainStepAdapter
: runs a SPI step inside the Domain engine by mapping DTOs and enums.Infrastructure\PluginDiscovery\ComposerExtraStepProviderDiscovery
: finds providers declared incomposer.json
.
Workflow Module Builder (Core wiring)
A default builder assembles a fully working module with sensible defaults (in-memory repositories, null scheduler/publisher, UUID run ID generator). You can also inject a SPI-based definition repository.
use AdachSoft\Workflow\PublicApi\Builder\WorkflowModuleBuilderDefault;
$facade = WorkflowModuleBuilderDefault::create()
// optionally inject your own repositories / services
// ->withRunRepository($runRepo)
// ->withRunDataRepository($runDataRepo)
// ->withStepRegistry($stepRegistry)
// ->withScheduler($scheduler)
// ->withEventPublisher($publisher)
// ->withIdGenerator($idGen)
->build();
To use an external SPI definition repository, pass it before build():
use AdachSoft\Workflow\PublicApi\Builder\WorkflowModuleBuilderDefault;
use Vendor\MyWorkflowSpi\FileBasedSpiWorkflowDefinitionRepository;
$spiRepo = new FileBasedSpiWorkflowDefinitionRepository(__DIR__ . '/workflows');
$facade = WorkflowModuleBuilderDefault::create()
->withDefinitionRepositorySpi($spiRepo)
->build();
For save() in read-only SPI repositories, the adapter maps NOT_SUPPORTED to InfrastructureConfigurationException
.
InlineScriptStep (type: "script")
Infrastructure step implementing Domain StepInterface
that executes inline PHP code from config.script
and returns its output as OutputDataCollection
.
- Input:
StepContextDto
withinput
,sharedState
, and optionalconfig
. - Config validation:
config.script
must be a non-empty string; otherwiseStepConfigValidationException
is thrown. - Output: array returned by the script is wrapped into
OutputDataCollection
. - Special key: the script may set
"$next.config"
in the output to influence the next step configuration.
Example snippet:
{
"step_id": "script_1",
"type": "script",
"config": { "script": "return ['content' => 'Hello', '$next.config' => ['retry_timeout' => 15]];" }
}
Declarative from→to mappings and MappingPolicy
Engine supports declarative mappings between sources and destinations:
- Sources:
$output.<key>
,$shared.<key>
- Destinations:
$input.<key>
,$shared.<key>
- Sugar:
"input": "content"
≅[ { "from": "$output.content", "to": "$input.content" } ]
"output": "alias"
saves the entire step output under$shared.alias
.
- Default behavior: if no mappings and no
input
sugar, the entire step output becomes the next step input (1:1). - Conflict policy per-step:
MappingPolicyEnum
with valuesFAIL
(do not overwrite) andOVERWRITE
(last write wins).
"$next.config" overlay for next step
- The current step may return
"$next.config": { ... }
in its output. - Engine stores it internally under shared key
__next.config.<next_step_id>
and merges with the next step base config according toMappingPolicyEnum
of that next step. - With
FAIL
, existing config keys are preserved; withOVERWRITE
, overlay replaces existing keys.
See functional tests for concrete scenarios under tests/Workflow/Functional/InlineScriptFunctionalTest.php
and fixtures under tests/Workflow/Fixtures/workflows/*
.
SPI: Workflow Definition Repository (External Providers)
Core exposes a Service Provider Interface (SPI) for Workflow Definition Repository so external libraries can provide definitions from files, DB, APIs, etc., without coupling to Domain.
- Interface:
AdachSoft\Workflow\Spi\Repository\Repository\SpiWorkflowDefinitionRepositoryInterface
- Exceptions:
AdachSoft\Workflow\Spi\Repository\Exception\SpiRepositoryException
(codes: NOT_FOUND, NOT_SUPPORTED, PERSISTENCE_ERROR, INVALID_DEFINITION) - DTO/VO/Enum/Collections: under
AdachSoft\Workflow\Spi\Repository\{Dto,Value,Enum,Collection}
Read the detailed integration guide:
- docs/SPI_WorkflowDefinitionRepository_Integration_Guide.md
Examples and Changelog
- See examples under
examples/spi-step-migration
for a minimal provider, factory, and step (updated to infer type from getType()). - See CHANGELOG.md for versioned details.