本文目录导读:

- 核心比喻:国家机密文件
- PHP 洋葱架构的典型分层(从内到外)
- 核心机制:依赖反转(Dependency Inversion Principle)
- 为什么不用传统三层架构(Controller -> Service -> Model)?
- 图例化理解
- 对 PHP 开发者的实际价值
- 总结一句话:
这是一个非常深刻的好问题。PHP 洋葱架构(Onion Architecture) 的核心思想是:让业务逻辑独立于技术实现,理解它的关键在于:由内向外依赖,外部向内调用。
下面我通过一个通俗易懂的比喻和具体的 PHP 代码例子来解释。
核心比喻:国家机密文件
想象一下,有一个国家最重要的 核心机密文件。
- 内层(核心层):文件本身(业务逻辑),它是最重要的,不能直接接触外界,也不关心你是用中文还是英文写(不关心数据库、框架等)。
- 中层(应用服务层):负责解读文件内容的文职人员,他知道文件该给谁看,但自己不动手去送(协调工作流)。
- 外层(基础设施层):送文件的快递员、存放文件的保险箱、张贴文件的公告板,他们具体干活,但不懂文件内容(具体技术实现)。
关键规则:
- 依赖方向(由内向外):核心层(文件)完全不知道外层(快递员)的存在,但外层(快递员)依赖核心层(文件内容)来工作。
- 控制反转(DIP):核心层定义接口(如何存储文件”),外层去实现这个接口,核心层不直接调用“快递员”,而是通过一个接口,让“快递员”去实现。
PHP 洋葱架构的典型分层(从内到外)
-
领域层(Domain Model / 核心)
- 实体、值对象、聚合、领域服务、仓储接口、领域事件。
- 职责:完全不依赖任何框架、数据库、Web 服务器,这是你的 “业务大脑”。
- PHP 例子:一个
Order类,里面有calculateTotal()方法,它不知道自己的数据是存在 MySQL 还是 Redis 里。
-
应用层(Application Service / 中层)
- 应用服务、DTO(数据传输对象)、用例。
- 职责:负责协调业务流程,它调用领域层的接口,但不执行具体的技术操作,它不包含业务规则,只编排业务规则。
- PHP 例子:
CreateOrderService类,它的execute()方法会调用OrderRepository(接口)的save()方法,但自己不去写INSERT SQL。
-
基础设施层(Infrastructure / 外层)
- 数据持久化(Eloquent、Doctrine)、外部 API 调用、缓存、邮件发送、文件系统操作。
- 职责:实现应用层和领域层定义的接口,与框架、数据库、第三方服务紧密耦合。
- PHP 例子:
MysqlOrderRepository实现了OrderRepository接口,里面使用 Laravel 的DB::table()或 Eloquent ORM。
-
表示层(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 开发者的实际价值
- 易测试:领域层和应用层可以脱离 Laravel 等框架进行纯单元测试。
- 易替换:换数据库、换缓存、甚至换框架(从 Laravel 换到 Symfony),只需要替换外层的“Adapter”,业务逻辑纹丝不动。
- 代码组织:大项目下,业务逻辑不会散落在各种 Service、Model、Helper 里,核心是
Domain下的实体和接口。 - 团队协作:后端业务专家可以专心写
Domain,框架专家专心实现Infrastructure。
总结一句话:
洋葱架构 = 把“业务核心”封装在最里面,用依赖反转(接口)隔离外部技术,由外向内依赖,从内向外调用。
这样,即使 PHP 版本升级、框架变更、数据库迁移,你的业务核心代码依然稳定如山。