PHP项目整洁架构分层

wen PHP项目 1

PHP项目整洁架构分层:从混乱到有序的实战指南

目录导读

  1. 什么是整洁架构?为何PHP项目需要它?
  2. 整洁架构的核心分层原则(附对比表)
  3. PHP项目分层落地实操(含代码示例)
  4. 常见反模式与解决方案(问答环节)
  5. 分层架构的长期收益

什么是整洁架构?为何PHP项目需要它?

整洁架构(Clean Architecture)由Robert C. Martin提出,核心思想是让业务逻辑与技术实现解耦,很多PHP项目(尤其是Laravel、Symfony框架项目)初期依赖简单,但随着功能增多,代码会退化为“泥球架构”——控制器直接调用数据库、业务逻辑散落在模板中。

PHP项目整洁架构分层

现实痛点:

  • 修改支付逻辑要动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项目,它能解决:

  1. 技术债务爆发:业务逻辑碎片化 -> 领域层统一管控
  2. 团队协作冲突:职责边界模糊 -> 各司其职(前端改界面层,后端改业务层)
  3. 测试覆盖率低:依赖外部服务 -> 内存测试纯业务逻辑

最后建议: 从下一个新模块(优惠券系统”)开始试点分层,使用PHP 8.1+结合RoadRunner或Swoole,在保持高性能的同时享受整洁架构的红利。

抱歉,评论功能暂时关闭!