PHP项目整洁架构分层:从混乱到有序的实战指南
目录导读
- 什么是整洁架构?为何PHP项目需要它?
- 整洁架构的核心分层原则(附对比表)
- PHP项目分层落地实操(含代码示例)
- 常见反模式与解决方案(问答环节)
- 分层架构的长期收益
什么是整洁架构?为何PHP项目需要它?
整洁架构(Clean Architecture)由Robert C. Martin提出,核心思想是让业务逻辑与技术实现解耦,很多PHP项目(尤其是Laravel、Symfony框架项目)初期依赖简单,但随着功能增多,代码会退化为“泥球架构”——控制器直接调用数据库、业务逻辑散落在模板中。

现实痛点:
- 修改支付逻辑要动Model、Controller、甚至JavaScript
- 换数据库(如MySQL转MongoDB)需重写半数代码
- 单元测试耗时是代码量的3倍(因为依赖无法mock)
整洁架构的价值:
- 业务独立于框架:框架可以换(如Laravel转Hyperf),业务代码不变
- 可测试性提升:领域逻辑可脱离HTTP请求单独测试
- 扩展性:新增支付网关只需新增一个接口实现类
整洁架构的核心分层原则
整洁架构通常分为四层(由内到外,依赖方向指向内层):
| 层级 | 名称 | 职责 | 禁止依赖 |
|---|---|---|---|
| 内层 | 领域层(Domain) | 实体、值对象、领域服务、仓储接口 | 无(纯业务逻辑) |
| 次内 | 应用层(Application) | 用例(Use Cases)、DTO、应用服务 | 领域层(仅依赖接口) |
| 次外 | 基础设施层(Infrastructure) | 数据库实现、第三方SDK、邮件发送 | 应用层(实现其接口) |
| 外层 | 界面层(Presentation) | HTTP控制器、CLI命令、API中间件 | 应用层(调用用例) |
关键规则:
- 依赖反转:外层依赖内层接口(比如仓库接口在领域层,实现在基础设施层)
- 跨层通信:内层不引用外层任何类(包括框架Facade)
PHP项目分层落地实操
步骤1:定义领域层(业务核心)
// 领域层:实体(Entity)
class Order implements EntityInterface {
private int $id;
private Money $total; // 值对象
private OrderStatus $status;
public function pay(): void {
if ($this->status !== OrderStatus::PENDING) {
throw new \DomainException('仅待支付订单可支付');
}
$this->status = OrderStatus::PAID;
}
}
// 领域层:仓储接口(不依赖ORM)
interface OrderRepositoryInterface {
public function findById(int $id): Order;
public function save(Order $order): void;
}
步骤2:实现应用层(用例)
// 应用层:支付用例
class PayOrderUseCase {
public function __construct(
private OrderRepositoryInterface $orderRepo,
private PaymentGatewayInterface $gateway
) {}
public function execute(PayOrderRequest $request): PayOrderResponse {
$order = $this->orderRepo->findById($request->orderId);
$order->pay(); // 领域逻辑
$this->gateway->charge($order->getTotal());
$this->orderRepo->save($order);
return new PayOrderResponse($order->getId());
}
}
步骤3:基础设施层实现(数据库+第三方)
// 基础设施层:使用Eloquent实现仓库
class EloquentOrderRepository implements OrderRepositoryInterface {
public function findById(int $id): Order {
$model = \App\Models\OrderModel::findOrFail($id);
return new Order(/* 从模型转换 */);
}
public function save(Order $order): void {
\App\Models\OrderModel::updateOrCreate(/* 转换逻辑 */);
}
}
步骤4:界面层调用
// 控制器(属于界面层)
class OrderController extends Controller {
public function pay(PayRequest $request, PayOrderUseCase $useCase) {
$response = $useCase->execute(new PayOrderRequest($request->orderId));
return response()->json(['success' => true, 'order_id' => $response->orderId]);
}
}
常见反模式与解决方案(问答环节)
Q1:这样分层不就跟框架绑定了吗?控制器还用了Laravel的Controller类。
A:是的,界面层可以依赖框架(这是它的职责),关键在业务核心层(领域+应用)不要引入框架代码,上述代码中领域层无任何Laravel痕迹,未来即使换成ThinkPHP,只需重写基础设施和界面层。
Q2:每个用例都要写一个UseCase类,会不会过度设计?
A:对于简单CRUD(比如修改用户昵称),可直接在控制器调用仓库,但涉及复杂业务逻辑的操作(支付、下单、退款)必须拆分为UseCase,建议使用“用例划分表”:
| 复杂度 | 示例 | 建议 |
|---|---|---|
| 纯查询 | 获取用户列表 | 直接在控制器或QueryService |
| 单一实体操作 | 修改邮箱 | 应用层简单服务 |
| 多实体协作 | 下单(扣库存+创订单+积分) | 必须UseCase |
Q3:跨层数据传递如何避免污染?
A:使用DTO(数据传输对象) 隔离层间依赖,例如应用层返回OrderDetailDTO(包含整数、字符串),而非直接返回领域实体(避免外层修改实体状态)。
Q4:这会影响性能吗?(比如ORM懒加载失效)
A:会有轻微影响,因为需要在仓库层手动处理关联数据,但收益远大于成本:
- 测试时可mock仓库接口,轻松覆盖99%逻辑
- 更换数据库只需改一个类(比如从MySQL切MongoDB,只需重写
OrderRepositoryInterface)
Q5:小团队是否需要这么重的架构?
A:建议渐进式:
- 初期:只分离领域层(Entity+RepositoryInterface)
- 中期:提取应用层UseCase
- 成熟期:完善基础设施层(Event、Command等)
分层架构的长期收益
整洁架构不是银弹,但对于生命周期超过6个月、涉及多人协作的PHP项目,它能解决:
- 技术债务爆发:业务逻辑碎片化 -> 领域层统一管控
- 团队协作冲突:职责边界模糊 -> 各司其职(前端改界面层,后端改业务层)
- 测试覆盖率低:依赖外部服务 -> 内存测试纯业务逻辑
最后建议: 从下一个新模块(优惠券系统”)开始试点分层,使用PHP 8.1+结合RoadRunner或Swoole,在保持高性能的同时享受整洁架构的红利。