PHP 怎么PHP六边形架构

wen PHP项目 2

本文目录导读:

PHP 怎么PHP六边形架构

  1. 六边形架构核心概念
  2. 实战示例:订单管理系统
  3. 六边形架构的优缺点
  4. 实际应用建议
  5. 最佳实践

我来详细讲解PHP中如何实现六边形架构(Hexagonal Architecture),也被称为端口与适配器架构。

六边形架构核心概念

基本结构

┌─────────────────────────────────────────────────────┐
│                      外部世界                        │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐         │
│  │   HTTP   │    │   CLI    │    │  Message │         │
│  │  适配器  │    │  适配器  │    │  适配器  │         │
│  └────┬────┘    └────┬────┘    └────┬────┘         │
└───────┼──────────────┼──────────────┼───────────────┘
        │              │              │
        ▼              ▼              ▼
┌─────────────────────────────────────────────────────┐
│                  六边形边界(端口)                    │
│  ┌─────────────────────────────────────────────┐   │
│  │                应用核心(领域)                │   │
│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐ │   │
│  │  │  用例    │    │  实体    │    │  服务    │ │   │
│  │  │ (UseCase)│    │(Entities)│   │(Services)│ │   │
│  │  └─────────┘    └─────────┘    └─────────┘ │   │
│  │  ┌─────────────────────────────────────┐   │   │
│  │  │          领域模型与业务规则            │   │   │
│  │  └─────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘
        │              │              │
        ▼              ▼              ▼
  ┌─────────┐    ┌─────────┐    ┌─────────┐
  │ 数据库   │    │  外部API │    │  文件系统│
  │ 适配器   │    │  适配器  │    │  适配器  │
  └─────────┘    └─────────┘    └─────────┘

实战示例:订单管理系统

目录结构

project/
├── src/
│   ├── Domain/                    # 领域层
│   │   ├── Model/                # 实体和值对象
│   │   │   ├── Order.php
│   │   │   ├── OrderId.php
│   │   │   └── Money.php
│   │   ├── Repository/           # 仓储接口(端口)
│   │   │   └── OrderRepository.php
│   │   ├── Service/              # 领域服务
│   │   │   └── OrderValidationService.php
│   │   └── Event/                # 领域事件
│   │       └── OrderCreated.php
│   │
│   ├── Application/              # 应用层
│   │   ├── UseCase/              # 用例(输入端口)
│   │   │   ├── CreateOrderUseCase.php
│   │   │   └── GetOrderUseCase.php
│   │   └── DTO/                  # 数据传输对象
│   │       ├── CreateOrderRequest.php
│   │       └── OrderResponse.php
│   │
│   ├── Infrastructure/           # 基础设施层(适配器)
│   │   ├── Persistence/          # 持久化适配器
│   │   │   ├── DoctrineOrderRepository.php
│   │   │   └── MySQLOrderRepository.php
│   │   ├── Http/                 # 输入适配器
│   │   │   ├── OrderController.php
│   │   │   └── OrderRequestValidator.php
│   │   └── Notification/         # 输出适配器
│   │       ├── EmailNotifier.php
│   │       └── SMSNotifier.php
│   │
│   └── Shared/                   # 共享模块
│       └── Exception/
│           └── OrderNotFoundException.php
│
└── config/                       # 配置目录
    └── dependencies.php

核心代码实现

领域层代码

<?php
// src/Domain/Model/Order.php
declare(strict_types=1);
namespace App\Domain\Model;
use App\Domain\Event\OrderCreated;
use DateTimeImmutable;
class Order
{
    private OrderId $id;
    private string $customerName;
    private Money $totalAmount;
    private array $items = [];
    private string $status;
    private DateTimeImmutable $createdAt;
    private array $events = [];
    private function __construct(
        OrderId $id,
        string $customerName,
        Money $totalAmount,
        array $items,
        DateTimeImmutable $createdAt
    ) {
        $this->id = $id;
        $this->customerName = $customerName;
        $this->totalAmount = $totalAmount;
        $this->items = $items;
        $this->status = 'pending';
        $this->createdAt = $createdAt;
    }
    // 静态工厂方法
    public static function create(
        OrderId $id,
        string $customerName,
        array $items,
        Money $amount
    ): self {
        $order = new self(
            $id,
            $customerName,
            $amount,
            $items,
            new DateTimeImmutable()
        );
        // 记录领域事件
        $order->recordEvent(new OrderCreated($id, $customerName, $amount));
        return $order;
    }
    public function confirm(): void
    {
        if ($this->status !== 'pending') {
            throw new \DomainException('Only pending orders can be confirmed.');
        }
        $this->status = 'confirmed';
    }
    public function cancel(): void
    {
        if ($this->status === 'shipped') {
            throw new \DomainException('Shipped orders cannot be cancelled.');
        }
        $this->status = 'cancelled';
    }
    // 拉取并清除领域事件
    public function pullEvents(): array
    {
        $events = $this->events;
        $this->events = [];
        return $events;
    }
    private function recordEvent(object $event): void
    {
        $this->events[] = $event;
    }
    // Getters...
    public function getId(): OrderId { return $this->id; }
    public function getCustomerName(): string { return $this->customerName; }
    public function getTotalAmount(): Money { return $this->totalAmount; }
    public function getStatus(): string { return $this->status; }
    public function getItems(): array { return $this->items; }
    public function getCreatedAt(): DateTimeImmutable { return $this->createdAt; }
}
// src/Domain/Model/OrderId.php
class OrderId
{
    private string $value;
    private function __construct(string $value)
    {
        if (!preg_match('/^ORD-\d+$/', $value)) {
            throw new \InvalidArgumentException('Invalid Order ID format.');
        }
        $this->value = $value;
    }
    public static function generate(): self
    {
        return new self('ORD-' . uniqid());
    }
    public static function fromString(string $id): self
    {
        return new self($id);
    }
    public function toString(): string
    {
        return $this->value;
    }
}
// src/Domain/Model/Money.php
class Money
{
    private float $amount;
    private string $currency;
    public function __construct(float $amount, string $currency = 'CNY')
    {
        if ($amount < 0) {
            throw new \InvalidArgumentException('Money cannot be negative.');
        }
        $this->amount = $amount;
        $this->currency = $currency;
    }
    public function getAmount(): float { return $this->amount; }
    public function getCurrency(): string { return $this->currency; }
}

端口(接口)定义

<?php
// src/Domain/Repository/OrderRepository.php
namespace App\Domain\Repository;
use App\Domain\Model\Order;
use App\Domain\Model\OrderId;
// 这是输出端口(Outbound Port)
interface OrderRepository
{
    public function save(Order $order): void;
    public function findById(OrderId $orderId): ?Order;
    public function findByCustomerName(string $customerName): array;
    public function delete(OrderId $orderId): void;
}
// src/Domain/Repository/OrderNotifier.php
interface OrderNotifier
{
    public function notifyOrderCreated(Order $order): void;
    public function notifyOrderConfirmed(Order $order): void;
    public function notifyOrderCancelled(Order $order): void;
}

应用层用例实现

<?php
// src/Application/UseCase/CreateOrderUseCase.php
namespace App\Application\UseCase;
use App\Application\DTO\CreateOrderRequest;
use App\Application\DTO\OrderResponse;
use App\Domain\Model\Order;
use App\Domain\Model\OrderId;
use App\Domain\Model\Money;
use App\Domain\Repository\OrderRepository;
use App\Domain\Repository\OrderNotifier;
// 这是输入端口(Inbound Port)的实现
class CreateOrderUseCase
{
    private OrderRepository $orderRepository;
    private OrderNotifier $orderNotifier;
    public function __construct(
        OrderRepository $orderRepository,
        OrderNotifier $orderNotifier
    ) {
        $this->orderRepository = $orderRepository;
        $this->orderNotifier = $orderNotifier;
    }
    public function execute(CreateOrderRequest $request): OrderResponse
    {
        // 计算订单总金额
        $totalAmount = 0;
        foreach ($request->getItems() as $item) {
            $totalAmount += $item['price'] * $item['quantity'];
        }
        // 创建订单
        $order = Order::create(
            OrderId::generate(),
            $request->getCustomerName(),
            $request->getItems(),
            new Money($totalAmount)
        );
        // 保存订单
        $this->orderRepository->save($order);
        // 触发通知
        foreach ($order->pullEvents() as $event) {
            $this->orderNotifier->notifyOrderCreated($order);
        }
        return OrderResponse::fromOrder($order);
    }
}
// src/Application/UseCase/GetOrderUseCase.php
class GetOrderUseCase
{
    private OrderRepository $orderRepository;
    public function __construct(OrderRepository $orderRepository)
    {
        $this->orderRepository = $orderRepository;
    }
    public function execute(string $orderId): OrderResponse
    {
        $order = $this->orderRepository->findById(OrderId::fromString($orderId));
        if (!$order) {
            throw new OrderNotFoundException("Order {$orderId} not found.");
        }
        return OrderResponse::fromOrder($order);
    }
}

DTO定义

<?php
// src/Application/DTO/CreateOrderRequest.php
namespace App\Application\DTO;
class CreateOrderRequest
{
    private string $customerName;
    private array $items;
    public function __construct(string $customerName, array $items)
    {
        $this->customerName = $customerName;
        $this->items = $items;
    }
    public function getCustomerName(): string { return $this->customerName; }
    public function getItems(): array { return $this->items; }
}
// src/Application/DTO/OrderResponse.php
class OrderResponse
{
    private string $id;
    private string $customerName;
    private float $totalAmount;
    private string $status;
    private string $createdAt;
    private function __construct(string $id, string $customerName, float $totalAmount, string $status, string $createdAt)
    {
        $this->id = $id;
        $this->customerName = $customerName;
        $this->totalAmount = $totalAmount;
        $this->status = $status;
        $this->createdAt = $createdAt;
    }
    public static function fromOrder(Order $order): self
    {
        return new self(
            $order->getId()->toString(),
            $order->getCustomerName(),
            $order->getTotalAmount()->getAmount(),
            $order->getStatus(),
            $order->getCreatedAt()->format('Y-m-d H:i:s')
        );
    }
    // Getters...
    public function toArray(): array
    {
        return get_object_vars($this);
    }
}

适配器实现(输出适配器)

<?php
// src/Infrastructure/Persistence/DoctrineOrderRepository.php
namespace App\Infrastructure\Persistence;
use App\Domain\Model\Order;
use App\Domain\Model\OrderId;
use App\Domain\Repository\OrderRepository;
// 输出适配器:持久化到数据库
class DoctrineOrderRepository implements OrderRepository
{
    private $entityManager;
    public function __construct($entityManager)
    {
        $this->entityManager = $entityManager;
    }
    public function save(Order $order): void
    {
        $this->entityManager->persist($order);
        $this->entityManager->flush();
    }
    public function findById(OrderId $orderId): ?Order
    {
        return $this->entityManager->find(Order::class, $orderId->toString());
    }
    public function findByCustomerName(string $customerName): array
    {
        $queryBuilder = $this->entityManager->createQueryBuilder();
        return $queryBuilder
            ->select('o')
            ->from(Order::class, 'o')
            ->where('o.customerName = :name')
            ->setParameter('name', $customerName)
            ->getQuery()
            ->getResult();
    }
    public function delete(OrderId $orderId): void
    {
        $order = $this->findById($orderId);
        if ($order) {
            $this->entityManager->remove($order);
            $this->entityManager->flush();
        }
    }
}
// src/Infrastructure/Notification/EmailNotifier.php
namespace App\Infrastructure\Notification;
use App\Domain\Model\Order;
use App\Domain\Repository\OrderNotifier;
// 输出适配器:发送邮件通知
class EmailNotifier implements OrderNotifier
{
    private $emailService;
    public function __construct($emailService)
    {
        $this->emailService = $emailService;
    }
    public function notifyOrderCreated(Order $order): void
    {
        $this->emailService->send(
            $order->getCustomerName(),
            '订单创建成功',
            sprintf('您的订单 %s 已创建,总金额 %.2f 元', 
                $order->getId()->toString(), 
                $order->getTotalAmount()->getAmount()
            )
        );
    }
    public function notifyOrderConfirmed(Order $order): void
    {
        $this->emailService->send(
            $order->getCustomerName(),
            '订单已确认',
            sprintf('您的订单 %s 已确认', $order->getId()->toString())
        );
    }
    public function notifyOrderCancelled(Order $order): void
    {
        $this->emailService->send(
            $order->getCustomerName(),
            '订单已取消',
            sprintf('您的订单 %s 已取消', $order->getId()->toString())
        );
    }
}

输入适配器(HTTP Controller)

<?php
// src/Infrastructure/Http/OrderController.php
namespace App\Infrastructure\Http;
use App\Application\DTO\CreateOrderRequest;
use App\Application\UseCase\CreateOrderUseCase;
use App\Application\UseCase\GetOrderUseCase;
use App\Shared\Exception\OrderNotFoundException;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
// 输入适配器:HTTP接口
class OrderController
{
    private CreateOrderUseCase $createOrderUseCase;
    private GetOrderUseCase $getOrderUseCase;
    public function __construct(
        CreateOrderUseCase $createOrderUseCase,
        GetOrderUseCase $getOrderUseCase
    ) {
        $this->createOrderUseCase = $createOrderUseCase;
        $this->getOrderUseCase = $getOrderUseCase;
    }
    public function createOrder(Request $request, Response $response): Response
    {
        try {
            $data = json_decode($request->getBody()->getContents(), true);
            // 验证输入
            $this->validateOrderRequest($data);
            // 创建请求DTO
            $createRequest = new CreateOrderRequest(
                $data['customer_name'],
                $data['items']
            );
            // 执行用例
            $result = $this->createOrderUseCase->execute($createRequest);
            // 返回成功响应
            $response->getBody()->write(json_encode([
                'status' => 'success',
                'data' => $result->toArray()
            ]));
            return $response
                ->withHeader('Content-Type', 'application/json')
                ->withStatus(201);
        } catch (\Exception $e) {
            return $this->errorResponse($response, $e->getMessage(), 400);
        }
    }
    public function getOrder(Request $request, Response $response, array $args): Response
    {
        try {
            $result = $this->getOrderUseCase->execute($args['id']);
            $response->getBody()->write(json_encode([
                'status' => 'success',
                'data' => $result->toArray()
            ]));
            return $response
                ->withHeader('Content-Type', 'application/json')
                ->withStatus(200);
        } catch (OrderNotFoundException $e) {
            return $this->errorResponse($response, $e->getMessage(), 404);
        } catch (\Exception $e) {
            return $this->errorResponse($response, '服务器内部错误', 500);
        }
    }
    private function validateOrderRequest(array $data): void
    {
        if (!isset($data['customer_name']) || empty($data['customer_name'])) {
            throw new \InvalidArgumentException('客户名称不能为空');
        }
        if (!isset($data['items']) || !is_array($data['items']) || empty($data['items'])) {
            throw new \InvalidArgumentException('订单项目不能为空');
        }
    }
    private function errorResponse(Response $response, string $message, int $status): Response
    {
        $response->getBody()->write(json_encode([
            'status' => 'error',
            'message' => $message
        ]));
        return $response
            ->withHeader('Content-Type', 'application/json')
            ->withStatus($status);
    }
}

依赖注入与配置

<?php
// config/dependencies.php
use App\Application\UseCase\CreateOrderUseCase;
use App\Application\UseCase\GetOrderUseCase;
use App\Infrastructure\Persistence\DoctrineOrderRepository;
use App\Infrastructure\Notification\EmailNotifier;
use App\Infrastructure\Http\OrderController;
$container = new \DI\Container();
// 配置依赖注入
$container->set('entityManager', function () {
    // Laravel/Lumen 中可以用 DB::connection()->getPdo()
    // Symfony 中可以用 $entityManager
    return require_once 'config/doctrine.php';
});
$container->set('emailService', function () {
    // 配置邮件服务
    return new \SomeEmailService([
        'host' => $_ENV['SMTP_HOST'],
        'port' => $_ENV['SMTP_PORT'],
    ]);
});
// 注入适配器
$container->set(OrderRepository::class, function ($c) {
    return new DoctrineOrderRepository($c->get('entityManager'));
});
$container->set(OrderNotifier::class, function ($c) {
    return new EmailNotifier($c->get('emailService'));
});
// 注入用例
$container->set(CreateOrderUseCase::class, function ($c) {
    return new CreateOrderUseCase(
        $c->get(OrderRepository::class),
        $c->get(OrderNotifier::class)
    );
});
$container->set(GetOrderUseCase::class, function ($c) {
    return new GetOrderUseCase($c->get(OrderRepository::class));
});
// 注入控制器
$container->set(OrderController::class, function ($c) {
    return new OrderController(
        $c->get(CreateOrderUseCase::class),
        $c->get(GetOrderUseCase::class)
    );
});
return $container;

使用示例

<?php
// public/index.php (入口文件)
require 'vendor/autoload.php';
require 'config/dependencies.php';
// 路由配置(类似 Laravel 路由)
$app = new \SomeFramework\App($container);
// 创建订单
$app->post('/orders', function ($request, $response) use ($container) {
    $controller = $container->get(OrderController::class);
    return $controller->createOrder($request, $response);
});
// 查看订单
$app->get('/orders/{id}', function ($request, $response, $args) use ($container) {
    $controller = $container->get(OrderController::class);
    return $controller->getOrder($request, $response, $args);
});
$app->run();

六边形架构的优缺点

✅ 优点

  1. 分离关注点:业务逻辑与外部依赖完全分离
  2. 可测试性:可以轻松进行单元测试,不需要外部基础设施
  3. 可维护性:修改外部依赖不影响核心业务逻辑
  4. 技术灵活性:可以随时替换数据库、消息系统等
  5. 独立开发:团队可以并行开发不同部分

❌ 缺点

  1. 复杂度增加:需要更多的抽象和接口
  2. 代码量增加:需要更多的 DTO、接口、适配器等
  3. 学习曲线:团队成员需要理解架构模式
  4. 过度设计风险:对于简单应用可能过于复杂

实际应用建议

<?php
// 简化版:对于小项目可以这样简化
// 只定义必要的接口,不过度设计
// 1. 定义接口(端口)
interface UserRepository {
    public function findById(int $id): ?User;
    public function save(User $user): void;
}
interface Mailer {
    public function send(string $to, string $subject, string $body): void;
}
// 2. 核心业务逻辑(不依赖任何框架)
class UserService {
    public function __construct(
        private UserRepository $users,
        private Mailer $mailer
    ) {}
    public function register(string $email, string $password): User {
        // 业务逻辑
        $user = User::create($email, password_hash($password, PASSWORD_BCRYPT));
        $this->users->save($user);
        $this->mailer->send($email, '欢迎注册', '感谢注册');
        return $user;
    }
}
// 3. 适配器(具体实现)
class DoctrineUserRepository implements UserRepository {
    public function findById(int $id): ?User {
        // Doctrine 实现
    }
    public function save(User $user): void {
        // Doctrine 实现
    }
}
class SymfonyMailer implements Mailer {
    public function send(string $to, string $subject, string $body): void {
        // Symfony Mailer 实现
    }
}

最佳实践

  1. 小而精:不需要每个类都加接口,只在真正需要的地方使用
  2. 依赖注入:使用容器管理依赖关系
  3. DTO 简洁:避免过度的 DTO 嵌套
  4. 错误处理:在适配器层处理框架异常,核心层只处理业务异常
  5. 测试优先:编写单元测试时只测试核心逻辑

六边形架构特别适合中大型应用,尤其是需要频繁变更外部依赖的项目,对于小型应用,可以采用简化版本或 MVP 模式。

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