bfg/repository
bfg/repository is a Laravel package that adds a clean repository layer between your application and Eloquent models, keeping queries organized and controllers lean. It supports reusable query logic and a testable data access structure.
Architecture fit. bfg/repository layers a repository abstraction over Eloquent models — a pattern that sits between controllers/services and the persistence layer. In a conventional Laravel architecture this is a legitimate seam: it lets you bind interfaces in the service container, swap implementations for tests, and centralize query logic that would otherwise be scattered across controllers and scopes. The fit is strongest where Eloquent models are used as active records today and you want a boundary for unit testing or for isolating read/write concerns. It is weakest in codebases that already lean heavily on the query builder's expressiveness or on Eloquent relationships — repository wrappers tend to either leak the builder back out or force a lowest-common-denominator API.
Integration feasibility. Composer-installable Laravel package with the usual auto-discovered service provider and publishable config. Feasibility is technically low-friction (install, publish config, extend base repository, bind interfaces), but organizational feasibility is the real constraint: with 1 star and 0 dependents, there is no evidence of production use, no ecosystem validation, and no community answers to consult when you hit edge cases. Treat adoption as adopting an internal library you happen to be able to pull from Packagist.
Technical risk. High, concentrated in provenance rather than in the pattern itself:
Model or Builder returns, which silently defeats the abstraction and creates a hidden coupling you discover at refactor time.upsert/saveOrFail semantics must be re-verified through the wrapper; anything the package does not surface becomes a workaround.Key questions to answer before committing.
Stack fit. Appropriate for a standard Laravel monolith or modular monolith on a supported PHP 8.x runtime, particularly where you want a testable persistence boundary without introducing Doctrine or a full CQRS split. Poor fit for codebases relying on complex Eloquent relationship graphs, global scopes, or heavy query-builder composition — the wrapper will either be bypassed or will cost more than it returns.
Migration path. Do not rip-and-replace. Introduce it as a new layer for new bounded contexts, leave existing models untouched, and wrap incrementally:
Compatibility. Verify the package's composer.json constraints against your Laravel and PHP versions before anything else; an unmaintained wrapper on a modern Laravel major is a likely blocker. Check for conflicts with existing service-provider bindings and any global model events or observers that assume direct Eloquent access.
Sequencing. Spike (1–2 days: install, exercise CRUD + pagination + eager loading + transactions) → interface definition and adapter layer → single-context pilot behind a feature flag → retrospective on abstraction leakage → broaden or abandon. Gate each step on a concrete finding, and be genuinely willing to abandon at the spike; the package's community signal does not justify sunk-cost escalation.
Maintenance. Assume you own it. With zero dependents and a single maintainer, plan for a quarterly compatibility review on each Laravel/PHP release and budget for at least one fork-and-patch cycle per year. Keep the fork in a private VCS or a path/vcs composer repository so upgrades are deliberate, not accidental. Document the base repository's behavior in your own ADR — future engineers will not find answers upstream.
Support. No meaningful community support channel, no Stack Overflow corpus, no issue-tracker velocity to rely on. Internal ownership must be explicit: a named team responsible for the adapter layer, with the package treated as a dependency of your abstraction, not as a framework component.
Scaling. The wrapper's runtime cost is negligible relative to Eloquent itself. The scaling risk is architectural: repositories that don't expose the builder invite N+1 queries when relationships are loaded lazily behind a generic find() method, and naive all()-style methods will not survive large tables. Enforce pagination and explicit eager-loading in your interfaces from day one.
Failure modes to design against.
Model/Builder), which erodes the testability benefit you adopted it for.Ramp-up. Mechanically trivial — a Laravel developer is productive in hours. The real ramp-up cost is judgment: teaching the team when to use the repository versus the model, and enforcing that discipline in review. Budget for a short internal guide and lint/architecture rules that flag direct model access in layers where the repository is mandated. Overall, treat this as a small, cheap-to-trial utility with a high ownership tax — worth a spike, not worth a platform commitment.
How can I help you explore Laravel packages today?