当您的PHP单体应用开始出现裂痕时
\n您是否曾遇到过这样的情况:将数据库从MySQL切换到PostgreSQL变成了一场为期一周的探索?或者当您需要添加一个新的API客户端,而代码与框架紧密耦合,以至于从头开始编写反而更简单?熟悉吗?
\n大多数PHP开发者迟早会在控制器和模型中遇到“意大利面条式代码”。像Laravel或Symfony这样的框架提供了便捷的工具,但它们也强制了与自身的紧密绑定。解决方案是——六边形架构(也称为“端口与适配器”或六边形架构)。
\n在本文中,我们将探讨如何构建一个不依赖于框架、数据库或外部服务的PHP应用。您将看到可以直接在项目中应用的代码示例。让我们开始吧!
\n\n什么是六边形架构,为什么您需要它?
\n想象一个六边形。中心是您的业务逻辑(领域)。周围是“端口”(接口),外部世界通过这些端口与核心通信。外部是“适配器”,它们实现这些端口:控制器、仓库、API客户端。
\n核心理念:应用核心对外部世界一无所知。它不导入Laravel,不了解PDO,也不调用curl。所有依赖关系都指向内部——适配器依赖于端口,而不是相反。
\n这种方法的优点显而易见:
\n- \n
- 可测试性:无需数据库和HTTP请求即可测试业务逻辑。 \n
- 灵活性:更换数据库、队列或框架只需编写一个新的适配器。 \n
- 代码纯净:您不会将“做什么”与“怎么做”混为一谈。 \n
听起来像魔法?让我们看看代码。
\n\n项目结构:基本骨架
\n我们以一个简单的场景为例:用户注册。我们需要将用户保存到数据库并发送欢迎邮件。
\n典型的目录结构如下:
\nsrc/\n├── Domain/\n│ ├── Entity/\n│ │ └── User.php\n│ ├── Repository/\n│ │ └── UserRepositoryInterface.php\n│ ├── Service/\n│ │ └── UserRegistrationService.php\n│ └── Event/\n│ └── UserRegisteredEvent.php\n├── Infrastructure/\n│ ├── Persistence/\n│ │ └── DoctrineUserRepository.php\n│ ├── Mail/\n│ │ └── SymfonyMailerAdapter.php\n│ └── Controller/\n│ └── RegisterUserController.php\n└── Application/\n └── UseCase/\n └── RegisterUserUseCase.php\n请注意:Domain是纯净的业务逻辑。Infrastructure是适配器。Application是用例。
步骤1:核心——领域与端口
\n实体(Entity)
\n我们从最简单的开始——用户。这只是一个包含数据的类,没有依赖关系。
\n<?php\nnamespace App\\Domain\\Entity;\n\nclass User\n{\n public function __construct(\n private readonly string $id,\n private string $name,\n private string $email\n ) {}\n\n public function getId(): string\n {\n return $this->id;\n }\n\n public function getName(): string\n {\n return $this->name;\n }\n\n public function getEmail(): string\n {\n return $this->email;\n }\n}\n\n端口——仓库接口
\n现在定义我们将如何保存用户。这是一个端口——将由适配器实现的接口。
\n<?php\nnamespace App\\Domain\\Repository;\n\nuse App\\Domain\\Entity\\User;\n\ninterface UserRepositoryInterface\n{\n public function save(User $user): void;\n public function findByEmail(string $email): ?User;\n}\n\n端口——邮件服务接口
\n类似地,用于发送邮件:
\n<?php\nnamespace App\\Domain\\Service;\n\nuse App\\