Чистая архитектура в PHP: пошаговое руководство.
Чистая архитектура (Clean Architecture), предложенная Робертом Мартином (дядя Боб), представляет собой подход к проектированию программного обеспечения, при котором бизнес-логика отделена от технических деталей: базы данных, фреймворков, пользовательского интерфейса и прочего окружения. Такой подход повышает устойчивость, тестируемость и сопровождаемость приложений. В данной статье будет рассмотрено, как реализовать принципы чистой архитектуры на языке PHP.
Основные принципы Clean Architecture
Чистая архитектура опирается на следующие принципы:
- Разделение ответственности — каждый слой отвечает за свою часть логики.
- Зависимости направлены внутрь — внешние слои зависят от внутренних, но не наоборот.
- Независимость от фреймворков — бизнес-логика не зависит от Laravel, Symfony и других.
- Тестируемость — каждый компонент может быть протестирован изолированно.
- Изоляция от внешних систем — база данных, API, UI — всё это инфраструктура, а не ядро.
Слои архитектуры
Слой
Описание
Frameworks
Фреймворки, HTTP-контроллеры, UI
Interface Adapters
DTO, контроллеры, репозитории
Application Core
Use Cases, сервисы приложения
Domain Core
Сущности, бизнес-логика
Пошаговое руководство по реализации
Шаг 1: Инициализация проекта
composer init
composer require --dev phpunit/phpunit
Шаг 2: Структура каталогов
src/
├── Domain/
│ ├── Entity/
│ ├── Repository/
│ └── ValueObject/
├── Application/
│ ├── UseCase/
│ └── Service/
├── Infrastructure/
│ ├── Persistence/
│ ├── Controllers/
│ └── Framework/
└── Shared/
└── Kernel/
Шаг 3: Пример сущности (Domain Layer)
// src/Domain/Entity/User.php
namespace AppDomainEntity;
class User {
private string $id;
private string $email;
public function __construct(string $id, string $email) {
$this->id = $id;
$this->email = $email;
}
public function getEmail(): string {
return $this->email;
}
}
Шаг 4: Интерфейс репозитория
// src/Domain/Repository/UserRepositoryInterface.php
namespace AppDomainRepository;
use AppDomainEntityUser;
interface UserRepositoryInterface {
public function findByEmail(string $email): ?User;
}
Шаг 5: Use Case (Application Layer)
// src/Application/UseCase/RegisterUser.php
namespace AppApplicationUseCase;
use AppDomainEntityUser;
use AppDomainRepositoryUserRepositoryInterface;
class RegisterUser {
private UserRepositoryInterface $repo;
public function __construct(UserRepositoryInterface $repo) {
$this->repo = $repo;
}
public function execute(string $email): User {
if ($this->repo->findByEmail($email)) {
throw new Exception("User already exists");
}
$user = new User(uniqid(), $email);
// save to repository (not shown here)
return $user;
}
}
Шаг 6: Реализация репозитория (Infrastructure Layer)
// src/Infrastructure/Persistence/InMemoryUserRepository.php
namespace AppInfrastructurePersistence;
use AppDomainEntityUser;
use AppDomainRepositoryUserRepositoryInterface;
class InMemoryUserRepository implements UserRepositoryInterface {
private array $users = [];
public function findByEmail(string $email): ?User {
foreach ($this->users as $user) {
if ($user->getEmail() === $email) {
return $user;
}
}
return null;
}
public function save(User $user): void {
$this->users[] = $user;
}
}
Шаг 7: Входная точка (Framework Layer)
// public/index.php
use AppApplicationUseCaseRegisterUser;
use AppInfrastructurePersistenceInMemoryUserRepository;
require_once __DIR__ . '/../vendor/autoload.php';
$repo = new InMemoryUserRepository();
$useCase = new RegisterUser($repo);
try {
$user = $useCase->execute('test@example.com');
echo "User registered: " . $user->getEmail();
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
Clean Architecture в PHP позволяет проектировать приложения с чётким разделением обязанностей, снижая связанность компонентов и упрощая тестирование. Несмотря на то что PHP изначально создавался как язык для веба, он отлично справляется с реализацией архитектурных подходов, присущих крупным программным системам.
Такой подход особенно эффективен при построении долгоживущих и масштабируемых систем, требующих гибкости и поддержки на протяжении многих лет.