PHP中的六边形架构:无痛代码整洁之道

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

你的代码是堡垒,而不是面条

\n\n

你是否曾经将项目从Laravel重写为Symfony,或者从MySQL迁移到PostgreSQL?如果是,你一定知道这种痛苦:控制器里充斥着SQL查询,模型知道关于HTTP请求的一切,而业务逻辑在更换框架时就会失效。熟悉吗?

\n\n

欢迎来到PHP中的六边形架构(也称为端口与适配器)的世界。这不是一个时髦的流行词,而是那些希望长久稳定运行的项目救生圈。在本文中,我们将探讨如何构建一个不惧怕变化的PHP应用程序:更换数据库、切换到其他缓存或新的API——这些都只是常规的适配器替换,而不是重写一半的代码。

\n\n

六边形架构到底是什么?

\n\n

想象一下,你的应用程序是一个核心(core),它不知道数据来自哪里:来自Web、控制台、Telegram机器人还是测试。在这个核心周围是端口(接口),外部是适配器(实现)。PHP中的六边形架构允许你将业务逻辑与基础设施代码分离。你只需编写一次规则,就可以在任何地方使用它们。

\n\n

关键原则:

\n
    \n
  • 依赖反转——上层代码不依赖于下层代码。一切反过来。
  • \n
  • 业务逻辑隔离——没有任何框架侵入你的用例。
  • \n
  • 可测试性——你可以在没有数据库和HTTP的情况下测试核心。
  • \n
\n\n

与Robert Martin的整洁架构不同,六边形架构更加灵活:它不规定层数,只是说:“内部是领域,外部是基础设施,通过接口连接它们”。

\n\n

项目结构:从混乱到有序

\n\n

让我们直接进入正题。以下是PHP中六边形架构的典型文件夹结构:

\n\n
src/\n├── Domain/\n│   ├── Entity/\n│   │   └── Order.php\n│   ├── Repository/\n│   │   └── OrderRepositoryInterface.php\n│   └── Service/\n│       └── OrderService.php\n├── Application/\n│   ├── UseCase/\n│   │   └── CreateOrderUseCase.php\n│   └── Port/\n│       ├── Input/\n│       │   └── CreateOrderInputPort.php\n│       └── Output/\n│           └── OrderOutputPort.php\n├── Infrastructure/\n│   ├── Persistence/\n│   │   └── DoctrineOrderRepository.php\n│   └── Http/\n│       └── OrderController.php\n└── Framework/\n    └── Symfony/\n        └── Kernel.php
\n\n

注意到了吗?Domain和Application不知道Doctrine、Symfony或MySQL的存在。它们只与接口打交道。适配器位于Infrastructure中,并通过依赖注入进行连接。

\n\n

步骤1:定义核心——领域

\n\n

我们从订单实体开始。没有ORM注解,没有违反不变量的setter:

\n\n
<?php\n// src/Domain/Entity/Order.php\nnamespace App\\Domain\\Entity;\n\nclass Order\n{\n    public function __construct(\n        private readonly string $id,\n        private readonly string $productName,\n        private float $totalAmount,\n        private string $status = 'pending'\n    ) {}\n\n    public function approve(): void\n    {\n        if ($this->status !== 'pending') {\n            throw new \\DomainException('Only pending orders can be approved.');\n        }\n        $this->status = 'approved';\n    }\n\n    public function getId(): string { return $this->id; }\n    public function getProductName(): string { return $this->productName; }\n    public function getTotalAmount(): float { return $this->totalAmount; }\n    public function getStatus(): string { return $this->status; }\n}
\n\n

这是仓库的端口(接口)。请注意:它不知道Doctrine或PDO:

\n\n
<?php\n// src/Domain/Repository/OrderRepositoryInterface.php\nnamespace App\\Domain\\Repository;\n\nuse App\\                

博客

书籍推荐