particle-academy/fancy-flow-php
Fancy Flow for PHP is a lightweight package for defining and running multi-step flows in Laravel apps. Express processes, transitions, and conditions in fluent PHP, with an API designed to stay simple, readable, and easy to test.
Architecture fit. The package provides a durable, graph-based workflow runtime for Laravel/PHP: a workflow is a graph of nodes, execution state is persisted as checkpoints, and RESUME reads those checkpoints to continue after failure or pause. Two primitives handle nesting — for_each (a per-item "lane") and subgraph (an inline graph). This maps cleanly onto the class of product requirements a TPM typically owns: multi-step onboarding, order/fulfilment pipelines, approval chains, per-record fan-out processing, and any long-running process where "where is it now?" and "can it resume?" are user-visible questions. The architecture is a genuine fit for Laravel applications that already hold domain state in Eloquent and want orchestration in-process rather than in an external queue-orchestrator.
Integration feasibility. No integration work is required by this release. The change is a defect fix in event propagation, and the release notes explicitly state consumers must do nothing "unless you count events." The practical integration surface is the event stream: node-status, node-message, log, node-checkpoint, and run-scoped run-start / run-end / run-error. Any host code that renders progress, writes audit records, or drives notifications subscribes to these.
Technical risk. The risk in this release is observability semantics, not durability:
each/0/body, each/1/body, and so on. A host that assumed every node-status referred to a top-level node — or that counted events to infer progress — will now see additional, differently-addressed events.for_each over N items with M nodes goes from emitting roughly top-level events only to emitting N×M node events plus logs and messages. Dashboards, log sinks, and websocket broadcasts sized against the old volume should be re-checked.node-status on the addressed nodes.Key questions.
node-status events, or key off node identifier alone rather than the full address?for_each fan-out, and what is the resulting event count at peak?Stack fit. Native Laravel/PHP — no sidecar, no separate runtime, no new infrastructure to operate. Workflows live alongside the application code and share its deployment, configuration, and data access. For a team already on Laravel this is the lowest-friction option; the trade-off is that workflow execution scales with the application tier rather than independently.
Migration path. Effectively none for this release. The only conditional work is for hosts that count events: audit every event listener and dashboard query for assumptions of "one event per node" or "node id is top-level," and rewrite them to read the address (the each/N/body prefix identifies the lane and item index). Everything else is a lockfile bump and a regression pass.
Compatibility. Behaviour is additive for most consumers — they now receive events they previously did not. It is a soft break for consumers that assumed a flat event space. Given the 0.x version line, the maintainer is within semver to ship this in a minor. The refactor moving the re-addressing logic into a single RunEvent::forParent() implementation, replacing two identical private copies, materially reduces the chance of the two executors diverging again — that is a structural compatibility improvement, not just a fix.
Sequencing.
for_each fan-out against the event pipeline (broadcast, log, persistence).Maintenance. Low surface area: one behavioural change, no configuration, no schema, no migration, no new dependency. The internal consolidation into forParent() lowers long-term maintenance risk. The standing maintenance obligation is our own: the package's test suite is weighted toward durability (resume correctness), which is exactly why this defect survived. Our acceptance tests must assert on emitted events — type, address, and payload — for every nested workflow we depend on.
Support. Minimal anticipated support load from the fix itself. The likely support vector is user-visible: any in-product progress display that previously showed nothing for for_each items will now populate, which may prompt "why did this change?" questions from operators, and conversely any custom display built around the old silence may now double-count or mislabel. Brief the support and operations teams on the new each/N/body addressing before rollout.
Scaling. The meaningful scaling consideration is event volume, not workflow throughput. Fan-out depth multiplies the event stream: item count × node count × event types, plus per-item logs and messages. Broadcast channels, websocket fan-out, and log ingestion are the components most likely to feel it first. Budget for this explicitly if for_each is used over large collections, and consider sampling or aggregating per-item node-message and log events at the consumer rather than the producer.
Failure modes.
run-start/run-end are deliberately not forwarded, a stalled item inside a for_each will not raise a lane-level error. Parent-level run-error remains the signal; per-item stalls must be detected by timeout on the addressed node events.Ramp-up. Low. Engineers need to internalise one convention — nested events are addressed, so read the address, never the count — and one constraint — lane lifecycles are not run lifecycles. Both are learnable in a single onboarding session. The larger ramp-up cost is organisational: establishing the discipline that observability contracts get their own tests, rather than being assumed correct because the durable path passes.
How can I help you explore Laravel packages today?