你的代码是堡垒,而不是面条
\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
与Robert Martin的整洁架构不同,六边形架构更加灵活:它不规定层数,只是说:“内部是领域,外部是基础设施,通过接口连接它们”。
\n\n项目结构:从混乱到有序
\n\n让我们直接进入正题。以下是PHP中六边形架构的典型文件夹结构:
\n\nsrc/\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中,并通过依赖注入进行连接。
步骤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\\