本文目录导读:

我来详细讲解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();
六边形架构的优缺点
✅ 优点
- 分离关注点:业务逻辑与外部依赖完全分离
- 可测试性:可以轻松进行单元测试,不需要外部基础设施
- 可维护性:修改外部依赖不影响核心业务逻辑
- 技术灵活性:可以随时替换数据库、消息系统等
- 独立开发:团队可以并行开发不同部分
❌ 缺点
- 复杂度增加:需要更多的抽象和接口
- 代码量增加:需要更多的 DTO、接口、适配器等
- 学习曲线:团队成员需要理解架构模式
- 过度设计风险:对于简单应用可能过于复杂
实际应用建议
<?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 实现
}
}
最佳实践
- 小而精:不需要每个类都加接口,只在真正需要的地方使用
- 依赖注入:使用容器管理依赖关系
- DTO 简洁:避免过度的 DTO 嵌套
- 错误处理:在适配器层处理框架异常,核心层只处理业务异常
- 测试优先:编写单元测试时只测试核心逻辑
六边形架构特别适合中大型应用,尤其是需要频繁变更外部依赖的项目,对于小型应用,可以采用简化版本或 MVP 模式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播