-
-
Notifications
You must be signed in to change notification settings - Fork 6
Architecture
In order for big projects to stay maintainable and scalable it is crucial to isolate specific
responsibilities into separate layers. That way, each part can be managed and updated
independently without causing cascading effects throughout the entire application.
The task of each layer is clearly defined, what it needs as parameters and what it returns.
There are multiple ways to structure a system and every article on the internet seems to describe layers with similar names in totally different ways.
This project uses three main layers: Application, Domain and Infrastructure.
The Application layer is the top-most layer and contains the code that is responsible
to deal with everything going out to the client or coming into the application after
the front-controller.
It contains middlewares, handles HTTP requests, handles errors and returns the HTTP response.
It is also interacts with the Domain layer to initiate specific
processes or request resources from service classes.
The Domain layer is the heart of the application encapsulating the core business logic
and rules.
Essential concepts and behaviours for the application to run as desired are defined here.
It contains the service classes, value objects, exceptions and is responsible for the
interaction with the infrastructure layer.
The Infrastructure layer is the bottom-most layer and is responsible for the communication
with external dependencies such as the database via repositories, the file system
and the mail server.
It needs be separated from the domain layer to increase testability and facilitate
the replacement of adapters to external dependencies with other implementations.
This layer should not contain logic and not be aware of the domain layer.
The classic approach would be to have three main folders inside the project directory src
to clearly separate the layers Application, Domain and Infrastructure.
Everything Domain-related
would be in the Domain folder and the repositories for each module / feature would be in the
Infrastructure folder.
For example:
├── Domain
│ ├── Module 1 # (e.g. Authentication)
│ │ ├── Service
│ ├── Module 2 # (e.g. Client)
│ │ ├── Service
│ └── etc.
├── Infrastructure
│ ├── Module 1 # Authentication
│ │ ├── Repository
│ ├── Module 2 # Client
│ │ ├── Repository
│ └── etc.
In bigger projects with lots of folders and modules this approach makes it unnecessarily hard to maintain. If a new feature or module is added or changed, the developer has to jump between the Domain and Infrastructure folders to make the necessary changes, and it's hard to keep an overview.
Scrolling between those different "Layer-folders" with identical module sub-folders is not
efficient in the practical world.
This is where the vertical slice architecture comes to the rescue.
Instead of separating the layers first and then have module sub-folders in each layer,
the vertical slice architecture suggests having the module folders in the same parent folder
(in our case "Domain") and separate the layers inside each module folder.
Every module is now a "Slice" containing all the layers by itself.
To have an even more concise and less cluttered codebase, the "Infrastructure" keyword can be
eliminated as long as the devs are aware that its components (e.g. "Repository") inherently belong
to a different layer than the Domain, business logic components (e.g. "Service").
The directory structure of before could look like this now:
├── Domain
│ ├── Module 1 # (e.g. Authentication)
│ │ ├── Service # Domain layer
│ │ ├── Repository # Infrastructure layer
│ ├── Module 2 # (e.g. Client)
│ │ ├── Service
│ │ ├── Repository
│ └── etc.
Components of the Application layer such as Actions could also be present in each of the slices,
but I have made the design choice to keep this whole layer separate in its own
src/Application
folder for now. This is to distinguish very clearly the components of that layer.
This way of separating the layers of the architecture is still relatively new to me and I don't
have a solution yet that makes it clear that every Application component belongs to an own
separate layer.
More on vertical slices architecture:
Slim app basics
- Composer
- Web Server config and Bootstrapping
- Dependency Injection
- Configuration
- Routing
- Middleware
- Architecture
- Single Responsibility Principle
- Action
- Domain
- Repository and Query Builder
Features
- Logging
- Validation
- Session and Flash
- Authentication
- Authorization
- Translations
- Mailing
- Console commands
- Database migrations
- Error handling
- Security
- API endpoint
- GitHub Actions
- Scrutinizer
- Coding standards fixer
- PHPStan static code analysis
Testing
Frontend
Other