PHP洋葱架构分层怎么理解

wen PHP项目 2

本文目录导读:

PHP洋葱架构分层怎么理解

  1. 核心比喻:国家机密文件
  2. PHP 洋葱架构的典型分层(从内到外)
  3. 核心机制:依赖反转(Dependency Inversion Principle)
  4. 为什么不用传统三层架构(Controller -> Service -> Model)?
  5. 图例化理解
  6. 对 PHP 开发者的实际价值
  7. 总结一句话:

这是一个非常深刻的好问题。PHP 洋葱架构(Onion Architecture) 的核心思想是:让业务逻辑独立于技术实现,理解它的关键在于:由内向外依赖,外部向内调用。

下面我通过一个通俗易懂的比喻和具体的 PHP 代码例子来解释。

核心比喻:国家机密文件

想象一下,有一个国家最重要的 核心机密文件

  1. 内层(核心层):文件本身(业务逻辑),它是最重要的,不能直接接触外界,也不关心你是用中文还是英文写(不关心数据库、框架等)。
  2. 中层(应用服务层):负责解读文件内容的文职人员,他知道文件该给谁看,但自己不动手去送(协调工作流)。
  3. 外层(基础设施层):送文件的快递员、存放文件的保险箱、张贴文件的公告板,他们具体干活,但不懂文件内容(具体技术实现)。

关键规则

  • 依赖方向(由内向外):核心层(文件)完全不知道外层(快递员)的存在,但外层(快递员)依赖核心层(文件内容)来工作。
  • 控制反转(DIP):核心层定义接口(如何存储文件”),外层去实现这个接口,核心层不直接调用“快递员”,而是通过一个接口,让“快递员”去实现。

PHP 洋葱架构的典型分层(从内到外)

  1. 领域层(Domain Model / 核心)

    • 实体、值对象、聚合、领域服务、仓储接口、领域事件。
    • 职责:完全不依赖任何框架、数据库、Web 服务器,这是你的 “业务大脑”
    • PHP 例子:一个 Order 类,里面有 calculateTotal() 方法,它不知道自己的数据是存在 MySQL 还是 Redis 里。
  2. 应用层(Application Service / 中层)

    • 应用服务、DTO(数据传输对象)、用例。
    • 职责:负责协调业务流程,它调用领域层的接口,但不执行具体的技术操作,它不包含业务规则,只编排业务规则。
    • PHP 例子CreateOrderService 类,它的 execute() 方法会调用 OrderRepository(接口)的 save() 方法,但自己不去写 INSERT SQL
  3. 基础设施层(Infrastructure / 外层)

    • 数据持久化(Eloquent、Doctrine)、外部 API 调用、缓存、邮件发送、文件系统操作。
    • 职责:实现应用层和领域层定义的接口,与框架、数据库、第三方服务紧密耦合。
    • PHP 例子MysqlOrderRepository 实现了 OrderRepository 接口,里面使用 Laravel 的 DB::table() 或 Eloquent ORM。
  4. 表示层(Presentation / 最外层)

    • HTTP 控制器、命令行脚本、API 路由、视图模板。
    • 职责:接收用户输入,调用应用层服务,返回响应,它是最脏的层次(耦合 Web 技术)。

核心机制:依赖反转(Dependency Inversion Principle)

这是洋葱架构的灵魂,内层定义接口,外层实现接口,外层依赖内层的抽象。

错误的方式(传统三层架构常见)

// 领域层(核心)直接依赖了具体的外部实现(MySQL),这是错误的!
class OrderService {
    public function createOrder($data) {
        // 直接 new 了一个数据库模型,导致核心层依赖了 Eloquent ORM
        $order = new EloquentOrder();
        $order->save();
    }
}

正确的洋葱方式领域层定义接口(内层抽象)

// src/Domain/Repository/OrderRepositoryInterface.php
namespace App\Domain\Repository;
interface OrderRepositoryInterface
{
    public function save(Order $order): void;
}

应用层使用该接口(中层编排)

// src/Application/Service/CreateOrderService.php
namespace App\Application\Service;
use App\Domain\Repository\OrderRepositoryInterface;
class CreateOrderService
{
    // 依赖抽象(接口),而不是具体实现
    private OrderRepositoryInterface $orderRepo;
    public function __construct(OrderRepositoryInterface $orderRepo)
    {
        $this->orderRepo = $orderRepo;
    }
    public function execute( array $orderData ): void
    {
        // 1. 执行业务逻辑
        $order = new \App\Domain\Model\Order( $orderData );
        // 2. 使用接口保存(不关心是 MySQL 还是 MongoDB)
        $this->orderRepo->save( $order );
    }
}

基础设施层实现接口(外层具体实现)

// src/Infrastructure/Persistence/MysqlOrderRepository.php
namespace App\Infrastructure\Persistence;
use App\Domain\Repository\OrderRepositoryInterface;
use App\Domain\Model\Order;
// 外层实现了内层定义的接口
class MysqlOrderRepository implements OrderRepositoryInterface
{
    public function save(Order $order): void
    {
        // 这里才真正使用 Laravel 的 Eloquent 或 DB Facade
        \App\Models\Order::create([
            'id' => $order->getId(),
            'total' => $order->getTotal(),
            // ...
        ]);
    }
}

使用依赖注入容器绑定(应用启动时)

// 通常在 AppServiceProvider 或 routes/web.php 中
$this->app->bind(OrderRepositoryInterface::class, MysqlOrderRepository::class);

为什么不用传统三层架构(Controller -> Service -> Model)?

传统三层架构(常见于初学者或快速开发)会面临一个问题:业务逻辑被技术实现绑架

  • 你的 UserService 里直接 new UserModel(), 用的是 Eloquent。
  • 如果将来公司决定从 MySQL 迁移到 MongoDB,或者从 Laravel 换到 Symfony,你需要修改业务逻辑层的代码
  • 单元测试困难:测试业务逻辑需要连接数据库。

图例化理解

  [ 外部依赖 ]     [  框架  ]     [  数据库  ]
       ↓                ↓              ↓
  ┌─────────────────────────────────────────┐
  │        (4) 表示层/Presentation         │  ← 最外层,依赖方向向内
  │     Controllers, Views, API Routes     │
  └────────────────┬────────────────────────┘
                   │ 调用应用层
  ┌────────────────▼────────────────────────┐
  │        (3) 基础设施层/Infrastructure    │  ← 外层,实现接口
  │     DB Logging, Email, Cache, API       │
  └────────────────┬────────────────────────┘
                   │ 实现领域层接口
  ┌────────────────▼────────────────────────┐
  │       (2) 应用层/Application            │  ← 中层,编排业务流程
  │     Application Services, DTOs          │
  └────────────────┬────────────────────────┘
                   │ 调用领域接口(无具体实现)
  ┌────────────────▼────────────────────────┐
  │         (1) 领域层/Domain               │  ← 最内层,核心业务逻辑
  │     Entities, Value Objects, Interfaces │
  └─────────────────────────────────────────┘
          ↑              ↑            ↑
    [ 业务规则 ]   [ 领域事件 ]  [ 仓储接口 ]

对 PHP 开发者的实际价值

  1. 易测试:领域层和应用层可以脱离 Laravel 等框架进行纯单元测试。
  2. 易替换:换数据库、换缓存、甚至换框架(从 Laravel 换到 Symfony),只需要替换外层的“Adapter”,业务逻辑纹丝不动。
  3. 代码组织:大项目下,业务逻辑不会散落在各种 Service、Model、Helper 里,核心是 Domain 下的实体和接口。
  4. 团队协作:后端业务专家可以专心写 Domain,框架专家专心实现 Infrastructure

总结一句话:

洋葱架构 = 把“业务核心”封装在最里面,用依赖反转(接口)隔离外部技术,由外向内依赖,从内向外调用。

这样,即使 PHP 版本升级、框架变更、数据库迁移,你的业务核心代码依然稳定如山。

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