common
Best practices for structuring backend project Architectures
There are several popular architectures for structuring backend projects. Here are a few:
- Layered (n-tier) architecture
- Hexagonal architecture (Ports and Adapters)
- Domain-Driven Design (DDD)
- Microservices architecture
- Clean Architecture
- Event-Driven Architecture
- Package by feature
- Package by type
-
Layered (n-tier) architecture:
This is the most common architecture, where the project is divided into layers such as presentation, business, and data access layers. Each layer has specific responsibilities and communicates with the layers directly above or below it.
Here's a basic example of how you might structure a project using a Layered (n-tier) architecture:
- ProjectName - PresentationLayer - Controllers - Views - DTOs - BusinessLayer - Services - Models - Interfaces - DataAccessLayer - Repositories - DatabaseContext - Tests - UnitTests - IntegrationTests
In this structure:
-
PresentationLayer
contains all the code related to the user interface and user interaction. This includes controllers, views, and Data Transfer Objects (DTOs) if you're using them. -
BusinessLayer
contains the business logic of your application. This includes services, models, and interfaces. -
DataAccessLayer
contains all the code related to data storage. This includes repositories and the database context. -
Tests
contains all the unit and integration tests for your application.
This is a simplified example. The exact structure can vary based on the needs of your project.
-
-
Hexagonal architecture (Ports and Adapters):
This architecture aims to create loosely coupled application components. The core application (business logic) is surrounded by several adapters (ports) that connect the application to necessary services like databases, UI, and so on.
- ProjectName
- Application
- Ports
- Inbound
- Outbound
- Services
- Domain
- Entities
- ValueObjects
- Repositories
- Infrastructure
- Adapters
- Inbound
- Controllers
- Presenters
- Outbound
- Repositories
- Configuration
- Tests
- UnitTests
- IntegrationTests
In this structure:
-
Application
contains the application services and the ports. Ports are interfaces that define the expected inputs (Inbound) and outputs (Outbound) for the application. -
Domain
contains all the business logic and business rules. This includes entities, value objects, and repository interfaces. -
Infrastructure
contains all the implementation details that the application should not be concerned with. This includes the adapters (concrete implementations of your ports), and any configuration code. -
Tests
contains all the unit and integration tests for your application.
This is a simplified example. The exact structure can vary based on the needs of your project.
3. Domain-Driven Design (DDD):
DDD focuses on the core domain and domain logic. It's based on the interactions between business domain objects (entities, value objects, aggregates, etc.) and is particularly useful for complex systems.
- ProjectName
- Application
- Commands
- Queries
- ApplicationServices
- Domain
- Entities
- ValueObjects
- Aggregates
- Repositories
- DomainServices
- Events
- Infrastructure
- Persistence
- Repositories
- DbContext
- Messaging
- Configuration
- Presentation
- Controllers
- Views
- DTOs
- Tests
- UnitTests
- IntegrationTests
In this structure:
-
Application
contains the application services, commands, and queries. These are the entry points to your domain logic. -
Domain
contains all the business logic and business rules. This includes entities, value objects, aggregates, repository interfaces, domain services, and domain events. -
Infrastructure
contains all the implementation details that the application should not be concerned with. This includes the persistence layer (like repositories and database context), messaging, and any configuration code. -
Presentation
contains all the code related to the user interface and user interaction. This includes controllers, views, and Data Transfer Objects (DTOs) if you're using them. -
Tests
contains all the unit and integration tests for your application.
This is a simplified example. The exact structure can vary based on the needs of your project.
4 Microservices architecture:
This architecture structures an application as a collection of loosely coupled services. Each service is a small, independent application that can be developed, deployed, and scaled independently.
- ProjectName
- Service1
- Application
- Domain
- Infrastructure
- Dockerfile
- Service2
- Application
- Domain
- Infrastructure
- Dockerfile
- Service3
- Application
- Domain
- Infrastructure
- Dockerfile
- Shared
- CommonLibraries
- DockerCompose
5 Serverless architecture:
In this architecture, the application is built and run in stateless compute containers that are event-triggered and fully managed by a third party (a cloud provider).
6 Clean Architecture:
This architecture emphasizes the separation of concerns, making the system more flexible, maintainable, and testable. It separates software elements into independent, interchangeable layers (entities, use cases, interface adapters, and frameworks).
- ProjectName
- Entities
- UseCases
- Interactors
- Ports
- InputPorts
- OutputPorts
- Controllers
- Presenters
- FrameworksAndDrivers
- Web
- Database
- Tests
- UnitTests
- IntegrationTests
In this structure:
-
Entities
contains the business objects of the application. -
UseCases
contains the business rules of the application. This includes Interactors (classes that implement the use cases) and Ports (interfaces used by the Interactors). -
Controllers
are part of the input adapters. They translate requests from the web into calls to the use cases. -
Presenters
are part of the output adapters. They format the data from the use cases for the web. -
FrameworksAndDrivers
contains all the details about the web framework, database, etc. These are the details that the application should not depend on. -
Tests
contains all the unit and integration tests for your application.
This is a simplified example. The exact structure can vary based on the needs of your project.
7. Event-Driven Architecture
- Description: This architecture revolves around the detection and handling of events. Systems are designed to detect and react to events, which are typically messages from other parts of the application.
- Benefits: Highly adaptable, scalable, and good for systems where asynchronous communication is a priority.
- Challenges: Complex debugging and testing, designing effective error handling can be challenging.
ProjectName
- Services
- Service1
- Events
- IncomingEvents
- OutgoingEvents
- Handlers
- Models
- Repositories
- Service2
...
- EventBus
- Handlers
- Interfaces
- SharedKernel
- CommonEvents
- Utilities
- APIGateway
- ExternalIntegrations
- Integration1
- Adapters
- Clients
- Integration2
...
- Database
- Migrations
- Models
- Repositories
- Tests
- UnitTests
- IntegrationTests
- EventFlowTests
Components Description
Services: Individual microservices or modules, each handling its own area of business logic.
- Events: Subcategories for incoming and outgoing events.
- Handlers: Event handlers that respond to incoming events and initiate business logic.
- Models: Domain models used within the service.
- Repositories: For interacting with the database.
EventBus: A component for routing events between services.
- Handlers: Handlers for routing events.
- Interfaces: Interfaces for services to interact with the event bus.
SharedKernel: Common code that can be used across different services.
- CommonEvents: Definitions of events common to multiple services.
- Utilities: Helper utilities and tools.
APIGateway: The entry point of the system, managing incoming requests and routing them to the appropriate services.
ExternalIntegrations: Integrations with external systems and services.
- Adapters: Adapters for converting data between formats.
- Clients: Clients for interacting with external APIs.
Database: Layers for database interaction.
- Migrations: Database migrations.
- Models: Data models.
- Repositories: Repositories for data access.
Tests: Testing layers, including unit tests, integration tests, and event flow tests.
This structure provides a clear separation of components, facilitating scalability and modifications in the system. It also supports the asynchronous and distributed nature of EDA, where event processing is a key element.
8 Package by feature (Пакетирование по функциям).
Вертикальное разделение кода, тестов и конфигов
Similar to "Microservices architecture", but in monolith.
project-name/
├── src/
│ ├── Http/
│ │ └── Controller/
│ ├── Auth/
│ │ ├── Command/
│ │ ├── Entity/
│ │ ├── Query/
│ │ ├── Test/
│ │ └── Config/
│ ├── Blog/
│ │ ├── Command/
│ │ ├── Entity/
│ │ ├── Query/
│ │ ├── Test/
│ │ └── Config/
│ ├── Shop/
│ │ ├── Command/
│ │ ├── Entity/
│ │ ├── Query/
│ │ ├── Test/
│ │ └── Config/
│ └── Mailer/
│ ├── Command/
│ ├── Entity/
│ ├── Query/
│ ├── Test/
│ └── Config/
└── tests/
└── Functional/
9 Package by type (Пакетирование по типам)
При этом подходе код организуется по типам или областям ответственности, таким как модели, контроллеры, репозитории и т.д. Классы одного типа группируются вместе, независимо от их функциональной принадлежности.
Пример структуры проекта на PHP с пакетированием по типам:
src/
Models/
User.php
Post.php
Invoice.php
...
Controllers/
AuthController.php
BlogController.php
PaymentController.php
...
Services/
AuthService.php
BlogService.php
PaymentService.php
...
Repositories/
UserRepository.php
PostRepository.php
InvoiceRepository.php
...
tests/
Models/
UserTest.php
PostTest.php
InvoiceTest.php
...
Controllers/
AuthControllerTest.php
BlogControllerTest.php
PaymentControllerTest.php
...
Services/
AuthServiceTest.php
BlogServiceTest.php
PaymentServiceTest.php
...
Repositories/
UserRepositoryTest.php
PostRepositoryTest.php
InvoiceRepositoryTest.php
...
В этом примере код разделен на пакеты Models
, Controllers
, Services
и Repositories
. Классы одного типа (модели, контроллеры и т.д.) объединены вместе, независимо от их функциональной принадлежности.
Sources:
-
Вертикальное разделение кода, тестов и конфигов. BeerPHP SPb Meetup #3 10 дек. 2021 г. https://elisdn.ru/blog/149/code-structure
-
Модульный PHP монолит: рецепт приготовления. 1 июн 2021 https://habr.com/ru/companies/ispring/articles/560074/
-
PHP NN #4: два доклада для поклонников Symfony и сочувствующих. 24 апр. 2021 г. https://youtu.be/2iPNz3p5Xiw?t=3787
Leave a reply