The following is a reference. A written account of how, at this present moment in my dev lifecycle, I structure applications. This is a post I hope to be able to look back on in six or twelve months and go, what the hell was I thinking. So, without further ado.
App\Controllers\MovieController App\Bindings\MovieModelRouteBinding App\Exceptions\MovieModelNotFoundException App\Models\MovieModelInterface App\Models\Eloquent\EloquentMovieModel App\Queries\MovieRepositoryQueryInterface App\Queries\MovieRepositoryQuery App\Queries\MovieRepositoryQueryBagTrait App\Repositories\MovieRepositoryInterface App\Repositories\EloquentCached\EloquentCachedMovieRepository App\Repositories\Eloquent\EloquentMovieRepository App\Transformers\MovieModelTransformer
That’s 13 classes for a single model and its’ controller. But it does come with a few advantages.
- The controller isn’t the only place where a repository is used. It can also be used in commands, events etc. All validation occurs within the repository, so there’s no inconsistencies.
- All requests to the repository (beyond CUD and single) require a corresponding Query. This has two advantages:
- The repository knows the query has been parsed, sanitised and validated. It can ask for a param without having to repeat any validation.
- As the query knows what can change, it generates the keys for caching in one place.
- By using interfaces, implementations can be swapped out with no impact. During initial dev, MovieRepositoryInterface => EloquentMovieRepository, but later on it’s swapped out for a CachedRepository (in effect a decorator around Eloquent) without having to touch the Eloquent or Interface classes.
- And the smaller classes, the route binding, transformer (using Fractal) etc, make testing each bit a cinch.
Who knows, this time next year I might have gone back to a “Model” file that is basically just a bunch of SQL queries. But for now, this process has done well for the last few projects.