Five years ago I wrote about how I structured applications. A lot has changed in five years. An old saying states you should be able to look back on your work of yesterday and wonder what you were thinking. So here is how I structure apps in 2020, what’s changed and what’s stayed the same.
Without further ado, here are the most common classes created for a fictional Movie type.
App\Http\Controllers\ApiMovieController App\Modules\Movie\Entities\Movie App\Modules\Movie\Commands\CreateMovieCommand App\Modules\Movie\CommandHandlers\CreateMovieCommandHandler App\Modules\Movie\Policies\MoviePolicy App\Modules\Movie\Readers\MovieReader App\Modules\Movie\Repositories\MovieRepository App\Modules\Movie\Repositories\MovieRepositoryInterface App\Modules\Movie\Resources\MovieResource
So what’s changed? Well the first thing is entities are now split into modules, with each module containing its own repositories, commands, resources et al. This makes it much simpler to identify all the parts of a specific module, even if there do tend to be strong links between modules (for instance the User module is referenced elsewhere).
The other big change is the move to Command Query Responsibility Segregation (CQRS). In 2015, I was handling a lot of the logic within the Controller. This was fine when the controller was the sole owner of an action. But as systems have grown, there are more and more events. Bundling authentication, logging, retries etc into the controller caused them to become unwieldy, especially in classes with many methods.
With CQRS, read requests go through a Reader. This is responsible for accepting query information, validating the input (and the user), and then returning data. All data is transformed through one or more Resources.
Any actions that may be performed, from creating a movie to submitting a vote on that entity, are now encapsulated as Commands. Each command contains all of the data needed for it to work including the user performing the action, the entity under control and any inputs. Looking at a command, you can see exactly what’s needed!
Commands are then routed through an event bus. This allows for logging of all actions, addition of transactions and retry controls, and authentication all without needing to touch the actual Command Handler that does the final work!
The system isn’t perfect, but it strikes a good balance between been self-documenting/protecting, and fast for rapid development.