pekral/arch-app-services
A structured application-services layer for Laravel that keeps controllers thin by moving use-case logic into dedicated, testable service classes with a consistent architecture.
Architecture fit
pekral/arch-app-services is an opinionated architecture toolkit for Laravel, not a runtime library. It supplies the scaffolding that enforces a specific layering convention — Actions as the unit of write-side behaviour, Data Validators as the boundary for input validation, and a Result pattern for outcomes — and then backs that convention with static analysis. This is a strong fit where the team has already bought into action-oriented Laravel (thin controllers, no business logic in models) and wants that convention made mechanically enforceable rather than documented in a wiki. It is a poor fit where the codebase is conventionally service/repository structured, since the PHPStan rules will flag existing code and the team will face a choice between mass refactoring and a large ignoreErrors baseline that defeats the purpose.
The 0.5.1 release does not add runtime capability; it refines the enforcement layer. Splitting a single combined rule into an Action rule and a Data Validator rule is architecturally correct — the two concerns have genuinely different namespaces and configurable scopes — and it lets a project adopt one convention without the other. The documentation additions (transactions, Result pattern, PHPStan rules) matter more than they look: an opinionated package lives or dies on how quickly a new engineer can infer the intended pattern, and previously the transaction/Result guidance was implicit.
Integration feasibility
The integration surface is narrow and low-risk: a handful of service container registrations, a neon config block for PHPStan, and package-level config supplying namespace prefixes. There is no database migration, no published config that must be reconciled, no middleware, no route registration. The main coupling is PHPStan — the package is only useful if static analysis runs in CI, so adoption requires PHPStan to be wired and gating in the pipeline. Without that, the package's value largely evaporates.
The 0.5.1 breaking change is contained by design: rule identifiers are unchanged, so existing ignoreErrors entries continue to work. Only projects that explicitly registered the removed combined class must edit their neon file, replacing one registration with two and supplying namespace arguments. That is a two-minute config edit per project, and the namespace arguments are an improvement in precision — the old rule presumably inferred scope, and the new rules state it.
Technical risk
Action/DataValidator namespaces and the Result pattern become load-bearing for the whole codebase's structure. Backing out later is expensive; the rules will have shaped dozens of classes by then.@pekral). Bus factor of one on a package that gates CI is a genuine supply-chain and continuity consideration; consider vendoring or forking rights as a contingency.Result pattern and transaction guidance were only just documented. Early adopters may have implemented subtly different interpretations that the new docs and rules will now flag.Key questions
actionNamespaces / dataValidatorNamespaces list each — mixed or nested conventions may need multiple registrations.ignoreErrors entries do we currently carry for the old identifier, and do any of them mask violations we would now rather fix than suppress?neon edit on the next breaking release?Result pattern a change to how new code returns outcomes, or a retrofit of existing return types? The documentation addition implies the former is expected but doesn't force the latter.Stack fit
Requires Laravel with PHPStan already in the toolchain and a codebase willing to adopt action-oriented structure. The package is additive — it registers rules and provides base classes/traits — so it does not conflict with existing service providers or with other Laravel architecture packages unless those also impose PHPStan rules over the same namespaces. Check for overlapping rules (e.g. another package enforcing controller or model purity) to avoid duplicate or contradictory errors on the same code.
Migration path
App\ on day one.Result pattern and transaction docs should become the reference for new Actions immediately; existing code follows as it's touched.If a project is on 0.5.x with the combined rule registered, the upgrade is mandatory-config: replace the single services entry with the two shown in the release notes, supplying the namespace arguments. Existing ignoreErrors identifiers carry over unchanged, so the baseline does not need regeneration — verify this with a CI run rather than trusting it.
Compatibility
No runtime or framework-version constraints were surfaced in this release; the change is confined to the PHPStan rule layer and docs. The one hard compatibility break is the removed class NoHttpExceptionsInActionsAndDataValidatorsRule — any neon file, shared config repo, or CI template referencing it by class name will fail to boot PHPStan. Grep for the class name across all repos and shared CI configurations, not just application code. Note that a neon file referencing a missing class typically produces a container-compilation error rather than a skipped rule, so this fails loudly — good, but it will block the pipeline until fixed.
Sequencing
Pin to 0.5.1 exactly rather than a caret range, given the pre-1.0 breaking-change-in-patch precedent. Sequence: config change → CI verification → merge. Then, separately and later, evaluate whether a constrained range is safe once the package reaches 1.0. Do not bundle this upgrade with other dependency work, since a failing PHPStan container is easy to misattribute to an unrelated change.
Maintenance
Ongoing cost is low but non-zero: a handful of lines in a neon file, a config file, and periodic re-reading of release notes before upgrades. The real maintenance burden is not the package but the convention it enforces — every new engineer must learn Actions, Data Validators, and the Result pattern, and reviewers must hold the line. Budget onboarding documentation that points at the package's README, and treat the README as the source of truth rather than writing an internal variant that will drift.
Support
Single-maintainer upstream. For a package that gates CI, establish a contingency: either vendor a pinned copy, or confirm someone on the team can patch it locally if upstream goes quiet. Escalation path for rule false positives is ignoreErrors with an inline justification comment — require the comment, or the ignore list becomes unaccountable.
Scaling
Rules scale fine — PHPStan runtime grows with codebase size, not with rule count, and the split into two rules with explicit namespace arguments actually allows scoping to subsets. Configure PHPStan paths so analysis targets changed modules where possible. The organisational scaling risk is larger: as more teams and repos adopt this, the neon block and namespace conventions must be templated centrally. Maintain a shared CI config fragment so the next breaking release is a one-line change in one repo, not an audit across fifteen.
Failure modes
ignoreErrors accumulates and the architecture drifts back toward the old conventions under the appearance of compliance. Track baseline size.Result and transaction docs describe intent; if the rules lag the docs or vice versa, engineers get contradictory signals. When in doubt, the rule is the enforced truth.Ramp-up
Half a day for an engineer already fluent in Laravel and PHPStan: read the README, wire the two rules, run against one module. One to two weeks before a team is genuinely productive in the idiom — the syntax is trivial, but knowing where an Action ends and a Data Validator begins, and when to return a Result versus throw, takes a few real features to internalise. The 0.5.1 documentation additions materially reduce this ramp-up and are the strongest argument for upgrading promptly rather than sitting on 0.5.0.
How can I help you explore Laravel packages today?