Appearance
Back-end Architecture
TL;DR
MVC with:
- modules (not packages)
- event and listeners to communicate between modules
- slim controllers (invokable are preferred)
- models without getters and setters
- actions to make controllers slim
- jobs for queue and in some cases sync execution
We don’t use:
- repositories
- query classes (deprecated)
- prefer DI > Facade > helpers
Our application widely uses the Laravel Framework and MVC paradigm. So the architecture of our application is typical for Laravel apps, because Laravel provides the most value when you write things the way Laravel intended you to write. If there’s a documented way to achieve something, follow it. Whenever you do something differently, make sure you have a justification for why you didn’t follow the defaults.
Design Characteristics
Minimal complexity
The primary goal of software design should be to minimize complexity. Avoid making “clever” designs. Clever designs are usually hard to understand. Instead, make “simple” and “easy-to-understand” designs. If your design doesn’t let you safely ignore most other parts of the program when you’re immersed in one specific part, the design isn’t doing its job.
Ease of maintenance
Ease of maintenance means designing for the maintenance programmer. Continually imagine the questions a maintenance programmer would ask about the code you’re writing. Think of the maintenance programmer as your audience, and then design the system to be self-explanatory.
Minimal connectedness
Minimal connectedness means designing so that you hold connections among different parts of a program to a minimum. Use the principles of strong cohesion, loose coupling, and information hiding to design classes with as few interconnections as possible. A minimal connectedness minimizes work during integration, testing, and maintenance.
Use interfaces and dependency injection to create loose connection between different modules of your software.
Modules
Modular programming is a software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality.
Namespaces
IxDF application has 40+ Modules. Every Module usually (but not always) has Models, Controllers, Services, Jobs, Console Commands and a Service Provider (or few). Currently, we don’t create a new package for every Module or extract them into a new PSR-4 namespace, but we always use separate namespace/directory for every Module under App/Modules/ namespace. Examples (Geo Module):
\App\Modules\Geo\Console\Commands\UpdateGeographicalDatabase::class\App\Modules\Geo\Models\Country::class\App\Modules\Geo\Services\Geocoder::class
As you can see, the namespace schema is App\Modules\{ModuleName}\{ClassType}, where {ClassType} is a directory name, that you can usually find at the app directory of the default Laravel application (so, it’s not a classic DDD dir structure).
Communication between Modules
Laravel provides few ways to do it:
- Dependency injection using interfaces (it's ok to use interfaces from other modules)
- Events
- Jobs (imperative way, not recommended for Module decoupling)
If you need to use in a Module Foo a service from a Module Bar, you should always use DI and inject the instance using an interface: Modules should not know about implementations from other Modules.
If you need to run something in a Module Foo after something happened in a Module Bar, please use Events and Listeners. Please read our conventions about Events and Listeners.