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

v0.6.1 2025-10-23 08:09 UTC

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, optional nextStepId, 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 + metadata TagCollection).
  • 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 throw DuplicateStepTypeException.
  • Application\PluginBridge\StepRegistryPortAdapter: implements Domain StepRegistryPortInterface 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 in composer.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 with input, sharedState, and optional config.
  • Config validation: config.script must be a non-empty string; otherwise StepConfigValidationException 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 values FAIL (do not overwrite) and OVERWRITE (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 to MappingPolicyEnum of that next step.
  • With FAIL, existing config keys are preserved; with OVERWRITE, 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.