Architecture

Table of contents

Loading...

Introduction

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.

The slim-example-project, slim-api-starter and slim-starter, use three main layers inspired by the Domain Driven Design (DDD): Application, Domain and Infrastructure.

Application

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 (Action), handles errors and returns the HTTP response.

The Application layer is responsible for orchestrating the execution of business operations, delegating the actual business logic to the Domain layer.

Domain

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, exceptions and is responsible for the interaction with the infrastructure layer.

Infrastructure

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 to 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 (but may create and return DTOs from the Domain).

Layer separation

Clean architecture

The classic approach would be to have three main folders inside the project directory src to clearly separate the layers Application, Domain and Infrastructure (e.g. Slim-Skeleton).
Everything Application-related resides in the src/Application folder, Domain-related stuff in the src/Domain folder, and the repositories for each module / feature would be in the src/Infrastructure folder.
Example directory structure:

├── src
    ├── Application
    │   ├── Module 1        # (e.g. Authentication) 
    │   │   ├── Action
    │   ├── Module 2        # (e.g. Client)  
    │   │   ├── Action
    │   └── etc.
    ├── Domain
    │   ├── Module 1        # (e.g. Authentication) 
    │   │   ├── Service
    │   ├── Module 2        # (e.g. Client)  
    │   │   ├── Service
    │   └── etc.
    └── Infrastructure
        ├── Module 1        # Authentication
        │   ├── Repository
        ├── Module 2        # Client
        │   ├── Repository
        └── etc.

The issue with this approach

Having the layers separated first and then the modules in each layer makes it unnecessarily more difficult to maintain and keep an overview, especially for projects that have lots of folders and modules.

If a new feature or module is added or changed, the developer has to jump between the Application, Domain and Infrastructure folders to make the changes.
Scrolling between those different "layer-folders" and searching the right module folders in each layer is not efficient in the practical world.

There is a great solution to this, and it's called the vertical slice architecture.

Vertical slice architecture

Work in progress — the architecture is being refactored in the branch vs-architecture-refactoring. See the reddit discussion.

Instead of separating the layers first and then having a module folder in each layer, the vertical slice architecture suggests having the module folders in the same parent folder (e.g. src/Module) and separate the layers inside each module folder.
Since modules typically contain multiple features, we can go even further and separate the layers inside each feature folder.

Every feature is now a "Slice" containing all the layers by itself.

To have an even more concise and less cluttered codebase, the layer folder names ("Application", "Domain", "Infrastructure") can be omitted if it doesn't serve a grouping purpose (e.g. if the Domain only contains a services or the Application only actions). For this to work, the developer must be aware that the contents of e.g. "Service", "Action" or "Repository" folders inherently belong to different layers.

The directory structure would look like this now:

├── src
    ├── Core
    │   ├── Application
    │   │   ├── Middleware
    │   │   └── Responder
    │   ├── Domain
    │   │   ├── Exception
    │   │   └── Utility
    │   └── Infrastructure
    │       ├── Factory
    │       └── Utility
    └── Module
        └── {ModuleX}
            ├── Create
            │   ├── Application
            │   │   ├── Action # Single Action Controller
            │   │   └── Exception # Exceptions that belong to the Application layer
            │   ├── Domain
            │   │   ├── Exception # Domain exceptions
            │   │   └── Service # ClientCreator service
            │   └── Infrastructure
            │       ├── Exception # Infrastructure exceptions
            │       └── Repository # ClientCreatorRepository
            ├── Data # DTOs
            ├── Delete
            │   ├── Action # Action folder without named parent Application directory
            │   ├── Service # Service folder without named parent Domain directory
            │   └── Repository # Repository folder without named parent Infrastructure directory
            ├── Read
            │   ├── Action
            │   ├── Service
            │   └── Repository
            ├── Update
            │   ├── Action
            │   ├── Service
            │   └── Repository
            └── Validation 
                    └── Service # Shared service

The Directory-Structure contains an overview of the folder structure.

Flowchart

image

More on this topic:

^