Product Decisions This Supports
- Build vs. buy for workflow/stateful domain features: Any roadmap item where an entity moves through a defined lifecycle (order → paid → shipped → refunded, subscription → trialing → active → churned, KYC → pending → verified → rejected, ticket → open → escalated → resolved) is a candidate for a state machine rather than ad-hoc status columns and scattered
if checks. This package is the contract layer that lets you define those transitions once and swap implementations.
- Architecture decisions in the Sylius ecosystem: If you are building or extending Sylius-based commerce (or any Symfony app that already pulls in Sylius components), this is the canonical interface set the rest of the ecosystem codes against. Adopting it aligns your custom workflows with upstream bundles and third-party Sylius plugins instead of inventing a parallel abstraction.
- Library and internal-platform strategy: For teams publishing reusable bundles or shared domain packages, coding against
StateMachineAbstraction instead of a concrete engine (Symfony Workflow, Winzou, custom) keeps your package portable and lets consuming teams choose their own engine.
- Roadmap sequencing for stateful features: Forces the useful PM conversation early — what are the states, what are the legal transitions, who can trigger them, what guard conditions apply, what side effects fire on transition. That artifact is valuable regardless of engine choice and is exactly what this abstraction models (states, transitions, guards, callbacks).
- Use cases: order fulfillment pipelines, payment and refund lifecycles, approval chains, subscription dunning, document review workflows, inventory reservation states, onboarding/KYC flows.
- What it does not decide for you: persistence, UI, event transport, or the engine itself. It is an interface package — it does not ship a working state machine, so it is not a feature you can demo to stakeholders, only a foundation you build on.
When to Consider This Package
Consider it when all of the following hold:
- You are already in the Sylius/Symfony/PHP stack, or explicitly targeting it. This has no value outside that ecosystem.
- You need more than one state machine implementation to coexist, or you need your own bundle/library to work across engines and Sylius versions without hard-coupling.
- You are extending Sylius and want your custom transitions to interoperate with core bundles and community plugins that type-hint against these interfaces.
- Your team accepts an abstraction-only dependency — interfaces, no runtime behavior — and understands the concrete engine is a separate decision and separate dependency.
- You want a vendor-neutral contract so a future engine swap (e.g., Symfony Workflow → a custom or messaging-oriented engine) is a contained change rather than a rewrite.
Look elsewhere (or do not adopt) when:
- You need a working state machine today. Use Symfony Workflow, Winzou StateMachine, or a hand-rolled implementation; add this abstraction only if portability is a real requirement.
- You are outside PHP/Symfony or outside the Sylius sphere. Zero benefit, pure indirection.
- Your domain has exactly one workflow and no reuse ambition. A thin abstraction over a single engine is a tax: extra indirection, extra concepts for the team to learn, harder debugging, and IDE jump-to-definition that lands in an interface instead of behavior.
- Maturity signals matter to your org. This is a low-visibility, effectively internal-facing package with no dependents and minimal community traction. Treat it as "an interface set maintained upstream," not as a supported product with a roadmap, SLA, or community you can lean on. Vendor-risk review should note that you are depending on the Sylius maintainers continuing to publish it, and that you can vendor/fork the interfaces cheaply if they don't — which is the main mitigating factor.
- You need persistence, visualization, audit trails, or transition logging out of the box. None of that lives here; budget engineering for it explicitly.
How to Pitch It (Stakeholders)
For executives (one paragraph): This is a small, free (MIT), interface-only component from the Sylius commerce platform that standardizes how we describe business workflows — order lifecycles, payment states, approval chains — in code. It costs essentially nothing to adopt and it prevents a known expensive failure mode: workflow logic welded to one library so that changing it later means a rewrite. It is a standards decision, not a feature decision — it delivers no user-visible capability on its own, so it should be bundled into workflow-related roadmap items rather than funded separately. The risk is low and bounded: the code is trivial to fork if upstream ever stops maintaining it, and we take on no vendor lock-in by using it.
For engineering: This is the Sylius contract layer for state machines — states, transitions, guards, and callbacks as interfaces. Adopt it if we are extending Sylius (so our transitions interoperate with core and plugins) or if we are shipping internal bundles that must not hard-code an engine. Do not adopt it as a substitute for choosing an engine; we still pick Symfony Workflow or equivalent and implement these interfaces. Realistic cost is a day of integration plus ongoing conceptual overhead for developers who now debug through an interface boundary. Be explicit that it is abstraction-only — no persistence, no audit log, no visualization — so those remain scoped work. Given zero dependents and minimal external traction, treat upstream continuity as a low-but-nonzero risk and keep a fork option in the architecture notes.
Recommendation: Adopt conditionally — only as a supporting dependency inside Sylius-adjacent or reusable-bundle work, never as a standalone initiative, and never without a concrete engine chosen alongside it.