本文目录导读:

我将为你详细讲解PHP整洁架构(Clean Architecture)的实现方法,整洁架构是由Robert C. Martin(Uncle Bob)提出的,核心思想是依赖向内,外层依赖内层。
🏗️ 整洁架构的核心结构
graph TD
A[框架/UI层] --> B[适配器层]
B --> C[用例层]
C --> D[实体层]
D -.->|依赖倒置| C
C -.->|依赖倒置| B
目录结构示例
src/
├── Domain/ # 实体层(最内层)
│ ├── Entities/ # 核心业务实体
│ ├── ValueObjects/ # 值对象
│ └── Repositories/ # 仓储接口
├── Application/ # 用例层
│ ├── UseCases/ # 具体业务用例
│ ├── DTOs/ # 数据传输对象
│ └── Ports/ # 接口定义
├── Infrastructure/ # 适配器层
│ ├── Persistence/ # 数据库实现
│ ├── HTTP/ # HTTP客户端
│ └── Messaging/ # 消息队列
└── Presentation/ # 框架/UI层
├── Controllers/ # 控制器
├── Middleware/ # 中间件
└── Views/ # 视图
📦 完整实现示例
Domain层(实体层)
<?php
// src/Domain/Entities/User.php
namespace App\Domain\Entities;
use App\Domain\ValueObjects\Email;
use App\Domain\ValueObjects\UserId;
class User
{
private UserId $id;
private string $name;
private Email $email;
private \DateTimeImmutable $createdAt;
public function __construct(UserId $id, string $name, Email $email)
{
$this->id = $id;
$this->name = $name;
$this->email = $email;
$this->createdAt = new \DateTimeImmutable();
}
public function getId(): UserId
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function getEmail(): Email
{
return $this->email;
}
public function changeName(string $newName): void
{
if (strlen($newName) < 3) {
throw new \InvalidArgumentException('Name must be at least 3 characters');
}
$this->name = $newName;
}
}
<?php
// src/Domain/ValueObjects/Email.php
namespace App\Domain\ValueObjects;
class Email
{
private string $value;
public function __construct(string $value)
{
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email address');
}
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
public function __toString(): string
{
return $this->value;
}
}
<?php
// src/Domain/Repositories/UserRepositoryInterface.php
namespace App\Domain\Repositories;
use App\Domain\Entities\User;
use App\Domain\ValueObjects\UserId;
interface UserRepositoryInterface
{
public function findById(UserId $id): ?User;
public function save(User $user): void;
public function delete(UserId $id): void;
}
Application层(用例层)
<?php
// src/Application/UseCases/CreateUserUseCase.php
namespace App\Application\UseCases;
use App\Domain\Entities\User;
use App\Domain\Repositories\UserRepositoryInterface;
use App\Domain\ValueObjects\Email;
use App\Domain\ValueObjects\UserId;
use App\Application\DTOs\CreateUserRequest;
use App\Application\DTOs\CreateUserResponse;
class CreateUserUseCase
{
private UserRepositoryInterface $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function execute(CreateUserRequest $request): CreateUserResponse
{
// 业务逻辑验证
$user = new User(
new UserId(),
$request->getName(),
new Email($request->getEmail())
);
// 执行其他领域的业务规则
$this->validateUniqueEmail($user->getEmail());
// 保存实体
$this->userRepository->save($user);
return new CreateUserResponse($user);
}
private function validateUniqueEmail(Email $email): void
{
$existingUser = $this->userRepository->findByEmail($email);
if ($existingUser !== null) {
throw new \RuntimeException('Email already exists');
}
}
}
<?php
// src/Application/DTOs/CreateUserRequest.php
namespace App\Application\DTOs;
class CreateUserRequest
{
private string $name;
private string $email;
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
}
public function getName(): string
{
return $this->name;
}
public function getEmail(): string
{
return $this->email;
}
}
Infrastructure层(适配器层)
<?php
// src/Infrastructure/Persistence/DoctrineUserRepository.php
namespace App\Infrastructure\Persistence;
use App\Domain\Entities\User;
use App\Domain\Repositories\UserRepositoryInterface;
use App\Domain\ValueObjects\Email;
use App\Domain\ValueObjects\UserId;
use Doctrine\ORM\EntityManagerInterface;
class DoctrineUserRepository implements UserRepositoryInterface
{
private EntityManagerInterface $entityManager;
private string $entityClass = User::class;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function findById(UserId $id): ?User
{
try {
$repository = $this->entityManager->getRepository($this->entityClass);
$user = $repository->find($id);
// 转换为领域实体
return $user ? $this->toDomainEntity($user) : null;
} catch (\Exception $e) {
throw new \RuntimeException('Failed to find user: ' . $e->getMessage());
}
}
public function save(User $user): void
{
try {
// 将领域实体转换为Doctrine实体
$doctrineEntity = $this->toDoctrineEntity($user);
$this->entityManager->persist($doctrineEntity);
$this->entityManager->flush();
} catch (\Exception $e) {
throw new \RuntimeException('Failed to save user: ' . $e->getMessage());
}
}
public function delete(UserId $id): void
{
try {
$user = $this->entityManager->find($this->entityClass, $id);
if ($user) {
$this->entityManager->remove($user);
$this->entityManager->flush();
}
} catch (\Exception $e) {
throw new \RuntimeException('Failed to delete user: ' . $e->getMessage());
}
}
private function toDomainEntity($doctrineEntity): User
{
return new User(
new UserId($doctrineEntity->getId()),
$doctrineEntity->getName(),
new Email($doctrineEntity->getEmail())
);
}
private function toDoctrineEntity(User $user): object
{
$entity = new \App\Entities\User();
$entity->setName($user->getName());
$entity->setEmail($user->getEmail()->getValue());
return $entity;
}
}
Presentation层(框架/UI层)
<?php
// src/Presentation/Controllers/UserController.php
namespace App\Presentation\Controllers;
use App\Application\UseCases\CreateUserUseCase;
use App\Application\UseCases\GetUserUseCase;
use App\Application\DTOs\CreateUserRequest;
use App\Application\DTOs\GetUserRequest;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class UserController
{
private CreateUserUseCase $createUserUseCase;
private GetUserUseCase $getUserUseCase;
public function __construct(
CreateUserUseCase $createUserUseCase,
GetUserUseCase $getUserUseCase
) {
$this->createUserUseCase = $createUserUseCase;
$this->getUserUseCase = $getUserUseCase;
}
public function create(ServerRequestInterface $request): ResponseInterface
{
$body = json_decode((string)$request->getBody(), true);
try {
$userRequest = new CreateUserRequest(
$body['name'] ?? '',
$body['email'] ?? ''
);
$response = $this->createUserUseCase->execute($userRequest);
return $this->jsonResponse(201, [
'id' => (string)$response->getUser()->getId(),
'name' => $response->getUser()->getName(),
'email' => (string)$response->getUser()->getEmail()
]);
} catch (\InvalidArgumentException $e) {
return $this->jsonResponse(400, ['error' => $e->getMessage()]);
} catch (\RuntimeException $e) {
return $this->jsonResponse(500, ['error' => $e->getMessage()]);
}
}
private function jsonResponse(int $status, array $data): ResponseInterface
{
// 使用PSR-7 Response实现
$response = new \Nyholm\Psr7\Response();
$response->getBody()->write(json_encode($data));
return $response
->withStatus($status)
->withHeader('Content-Type', 'application/json');
}
}
🔧 依赖注入容器配置
<?php
// config/dependencies.php
use App\Application\UseCases\CreateUserUseCase;
use App\Application\UseCases\GetUserUseCase;
use App\Domain\Repositories\UserRepositoryInterface;
use App\Infrastructure\Persistence\DoctrineUserRepository;
use App\Presentation\Controllers\UserController;
return [
UserRepositoryInterface::class => function($container) {
return new DoctrineUserRepository($container->get('doctrine_em'));
},
CreateUserUseCase::class => function($container) {
return new CreateUserUseCase(
$container->get(UserRepositoryInterface::class)
);
},
GetUserUseCase::class => function($container) {
return new GetUserUseCase(
$container->get(UserRepositoryInterface::class)
);
},
UserController::class => function($container) {
return new UserController(
$container->get(CreateUserUseCase::class),
$container->get(GetUserUseCase::class)
);
}
];
📚 最佳实践
依赖方向规则
- 依赖只能向内:外层可以依赖内层,内层不能依赖外层
- 依赖倒置原则:面向接口编程,而不是具体实现
边界划分
✅ 正确的依赖方向
Controller → UseCase → Domain Entity
Repository Interface → Domain Entity
❌ 错误的依赖方向
Domain Entity → Controller
Controller → Repository Implementation
分层验证
// 控制器层验证(输入验证)
class UserController
{
public function create(Request $request)
{
$this->validator->validate($request, [
'email' => 'required|email',
'name' => 'required|min:3'
]);
// ... 传递到UseCase
}
}
// 用例层验证(业务规则)
class CreateUserUseCase
{
public function execute(CreateUserRequest $request)
{
// 验证业务规则
if ($this->userRepository->emailExists($request->getEmail())) {
throw new BusinessRuleException('Email already registered');
}
// ... 创建用户
}
}
// 实体层验证(数据完整性)
class User
{
public function __construct(UserId $id, string $name, Email $email)
{
if (strlen($name) < 3) {
throw new \InvalidArgumentException('Name too short');
}
// ...
}
}
使用服务提供者
// app/Providers/UserServiceProvider.php
class UserServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(UserRepositoryInterface::class, function() {
return new RedisUserRepository($this->app->make('redis'));
});
}
}
🎯 优势和不足
优势
✅ 可测试性高:核心业务逻辑不依赖基础设施
✅ 框架无关:可以轻松切换框架
✅ 关注点分离:每个层只关注自己的职责
✅ 易于维护:业务逻辑集中在核心层
不足
❌ 代码量增加:需要更多接口和DTO
❌ 初期开发速度慢:需要更多的规划和设计
❌ 学习曲线陡峭:需要理解抽象概念
📊 实际项目中的简化方案
对于中小型项目,可以适度简化:
// 简化的分层,但保持核心原则
src/
├── Domain/ # 核心业务(实体 + 接口)
├── Application/ # 用例
├── Infrastructure/ # 存储实现
└── Presentation/ # 控制器
// 简化示例:直接使用具体类而不是接口(如果不需要多实现)
class UserService
{
private UserRepository $userRepository;
// 但保持依赖注入
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
}
💡 推荐工具和框架
- Container:PHP-DI、Laravel Container
- ORM:Doctrine、Eloquent(在适当隔离后使用)
- Validation:Symfony Validator
- Testing:PHPUnit、Mockery
整洁架构的核心是理解其原则并灵活应用,而不是机械地复制结构,根据项目规模选择合适的复杂度,确保代码清晰、可维护。