PHP 怎么PHP整洁架构

wen PHP项目 3

本文目录导读:

PHP 怎么PHP整洁架构

  1. 🏗️ 整洁架构的核心结构
  2. 📦 完整实现示例
  3. 🔧 依赖注入容器配置
  4. 📚 最佳实践
  5. 🎯 优势和不足
  6. 📊 实际项目中的简化方案
  7. 💡 推荐工具和框架

我将为你详细讲解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

整洁架构的核心是理解其原则并灵活应用,而不是机械地复制结构,根据项目规模选择合适的复杂度,确保代码清晰、可维护。

抱歉,评论功能暂时关闭!