roundly-consulting/lifecycle-for-laravel
Lifecycle for Laravel adds configurable lifecycles to Eloquent models: define states, allowed transitions, guards, and hooks so business rules stay organized, predictable, and testable instead of scattered across controllers, observers, and service classes.
Architecture fit. The package sits in the domain layer of a Laravel application — transition/state history with polymorphic actor attribution, exposed through Eloquent API Resources. Release 1.1.0 tightens the read/render path rather than expanding the state machine: actor rendering becomes a named, configurable resource, and history rows render without touching the actor table. This is idiomatic Laravel shape (config-driven resolution, a JsonResource contract, a package-specific exception), and it introduces no new runtime component — no queue, cache, or external service — so it composes with existing app architecture without infrastructure change.
Integration feasibility. High. The default behavior works with zero configuration; the config key matters only if you want to add fields (name, avatar) to the actor payload. Integration effort is a drop-in JsonResource subclass plus one config entry, assuming the config file was published and needs the new key merged in. No schema migration is indicated.
Technical risk.
actor.type (and last_transition.actor.type) changes from the morph class to the class basename: App\Models\User → User, and with a morph map, the alias (user) → User (basename of the mapped class). Any client, snapshot test, TS enum, OpenAPI schema, or webhook consumer keyed on type changes behavior silently. Treat as breaking for API consumers regardless of the version number.InvalidLifecycleConfigurationException::notAResource fires for non-instantiable or non-JsonResource values — but from boot, container resolution, or first history render? Worst case is a production 500 on a history endpoint rather than a deploy-time failure.class_basename strips namespaces, so two models named User in different namespaces render identically. Low likelihood, but the value is no longer a unique type discriminator.TransitionActorResource wraps the TransitionRecord and loads no actor — good. A custom resource that adds name/avatar will lazy-load per row unless actors are eager-loaded, reintroducing exactly the N+1 this release avoided.Key questions.
actor.type change documented as breaking, and is there a legacy-output flag or planned 2.0?lifecycle.resources.actor resolved and validated, and what is the production failure surface?TransitionRecord (as stated) rather than the actor model — and therefore what can it safely access without a query?^1.6 signal any shift in the supported Laravel/PHP matrix?Stack fit. Laravel + Eloquent with a JSON API surface. Fits any app already returning resources from history endpoints. Morph-map usage is the deciding variable for how disruptive the type change is — apps without a morph map move from fully-qualified class names to basenames; apps with one move from aliases to basenames.
Migration path.
actor.type and last_transition.actor.type — tests, OpenAPI specs, TS types, mobile clients, partner integrations.'resources' => ['actor' => null] (or a custom class) so intent is recorded rather than implicit.Compatibility. No schema migration indicated. DB behavior unchanged — the PostgreSQL/MySQL CI legs moving to tmpfs is a CI speed change only. The testing ^1.6 bump is dev-only. The only consumer-visible break is the type value derivation.
Sequencing. Non-prod upgrade → fixture diff → consumer notification or compat resource → config merge and config:cache on deploy → production rollout → monitor history-endpoint 5xx and client-side parse/type errors (the latter won't show up in server metrics).
Maintenance. Low ongoing cost: one config key and, if used, one custom resource class in app code. Guard it with a contract test asserting the actor JSON shape, so future package changes to TransitionActorResource can't silently alter your public API. Add the config key to the deploy checklist — a stale config:cache after upgrade leaves the key absent, which is benign (falls back to the packaged resource), but a cached config naming a renamed class throws.
Support. One new failure mode to document: InvalidLifecycleConfigurationException::notAResource — typo in the class name, class not autoloadable, class not extending JsonResource, or a non-blank non-class string. Most of the near-term support load, however, will come from client teams reacting to changed type values, not from the package itself.
Scaling. Neutral-to-positive. Because TransitionActorResource wraps the TransitionRecord, history endpoints keep a flat query count regardless of page size — no N+1 regression on the default path. The exception is custom resources that surface actor attributes: those lazy-load per row on a paginated list, so either eager-load actors or denormalize the fields onto the transition record.
Failure modes.
lifecycle.resources.actor → exception at resolution; severity depends on timing (deploy-time vs. per-request 500).type for clients.type becomes a basename of the mapped class, which may match neither the old alias nor the old class name; migration notes must state this precisely.type values → client-side errors invisible to server monitoring; client telemetry is required to detect it.Ramp-up. Roughly half a day of backend work including tests for the config key and optional resource. Budget the larger share of effort for cross-team coordination on the type change and for contract tests that lock the JSON shape, plus a short API changelog entry explaining the old and new values for both morph-map and non-morph-map setups.
How can I help you explore Laravel packages today?