6. 洋葱架构

6.1 层次结构

┌─────────────────────────────────────┐
│   Infrastructure Layer              │  ← 框架、ORM、外部依赖
│  ┌───────────────────────────────┐  │
│  │   Application Layer            │  │  ← 用例、应用服务
│  │  ┌─────────────────────────┐  │  │
│  │  │   Domain Layer          │  │  │  ← 领域模型、业务规则
│  │  │  ┌───────────────────┐  │  │  │
│  │  │  │   Domain Model    │  │  │  │  ← 实体、值对象
│  │  │  └───────────────────┘  │  │  │
│  │  └─────────────────────────┘  │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘

6.2 依赖规则

  • 核心原则: 依赖只能从外层指向内层
  • Domain Layer: 不依赖任何外部层
  • Application Layer: 只依赖 Domain Layer
  • Infrastructure Layer: 实现内层定义的接口
<?php
// ✅ 正确:Infrastructure 依赖 Domain
namespace Infrastructure;
use Domain\OrderRepository; // 依赖接口

class MySQLOrderRepository implements OrderRepository {
    // 实现细节
}

// ❌ 错误:Domain 依赖 Infrastructure
namespace Domain;
use Infrastructure\MySQLConnection; // 违反依赖规则

class Order {
    private MySQLConnection $db; // 不应直接依赖基础设施
}

依赖规则(Dependency Rule),是 DDD 和整洁架构的核心原则。核心思想如下:

核心理念

依赖方向只能从外向内,内层定义接口(契约),外层实现接口。业务核心(领域层)不依赖任何技术细节。

分层依赖方向

┌─────────────────────────────────────────────┐
│ Infrastructure Layer (基础设施层) │
│ MySQL/Redis/HTTP/Email (技术实现) │
│ ↓ 依赖 (implements) │
├─────────────────────────────────────────────┤
│ Application Layer (应用层) │
│ Use Cases / Application Services │
│ ↓ 依赖 (uses) │
├─────────────────────────────────────────────┤
│ Domain Layer (领域层) ← 核心! │
│ 实体/值对象/领域服务/接口定义 │
│ (不依赖任何外层!) │
└─────────────────────────────────────────────┘

依赖方向:Infrastructure → Application → Domain
外层 中层 内层

关键规则

✅ 正确:外层依赖内层

// Infrastructure 层(外层)
namespace Infrastructure;
use Domain\OrderRepository; // ✅ 依赖领域层的接口

class MySQLOrderRepository implements OrderRepository {
public function save(Order $order): void {
// 使用 MySQL 实现
$this->pdo->execute(‘INSERT INTO orders …’);
}
}

❌ 错误:内层依赖外层

// Domain 层(内层)
namespace Domain;
use Infrastructure\MySQLConnection; // ❌ 违反依赖规则!

class Order {
private MySQLConnection $db; // ❌ 领域层不应知道 MySQL 的存在

  public function save(): void {
      $this->db->execute('INSERT ...');  // ❌ 领域逻辑耦合数据库
  }

}

依赖倒置原则(DIP)

高层模块不依赖低层模块,两者都依赖抽象(接口)。

传统方式(违反依赖规则)

// 业务逻辑直接依赖技术实现
class OrderService {
private MySQLDatabase $db; // ❌ 直接依赖具体实现

  public function createOrder(Order $order): void {
      $this->db->insert('orders', $order->toArray());
  }

}

// 问题:
// 1. 无法切换数据库(强耦合 MySQL)
// 2. 无法测试(必须连接真实数据库)
// 3. 业务逻辑依赖技术细节

DDD 方式(遵循依赖规则)

// 领域层:定义接口(抽象)
namespace Domain;

interface OrderRepository {
public function save(Order $order): void;
public function findById(OrderId $id): ?Order;
}

// 应用层:依赖接口
namespace Application;
use Domain\OrderRepository; // ✅ 依赖抽象

class CreateOrderUseCase {
public function __construct(
private OrderRepository $orderRepo // ✅ 依赖接口,不是实现
) {}

  public function execute(CreateOrderRequest $request): void {
      $order = Order::create(...);
      $this->orderRepo->save($order);  // 不知道具体是 MySQL/Mongo
  }

}

// 基础设施层:实现接口
namespace Infrastructure;
use Domain\OrderRepository; // ✅ 依赖领域层接口

class MySQLOrderRepository implements OrderRepository {
public function save(Order $order): void {
// MySQL 实现细节
}
}

依赖注入(连接各层)

// 在入口文件(Composition Root)组装依赖
$pdo = new PDO(‘mysql:…’);
orderRepo=newMySQLOrderRepository(orderRepo = new MySQLOrderRepository(orderRepo=newMySQLOrderRepository(pdo); // 具体实现
useCase=newCreateOrderUseCase(useCase = new CreateOrderUseCase(useCase=newCreateOrderUseCase(orderRepo); // 注入接口
controller=newOrderController(controller = new OrderController(controller=newOrderController(useCase);

// 业务代码中永远不知道具体实现!

分层职责和依赖
┌────────────────┬──────────────┬────────────────────────────┬─────────────────────────────────────┐
│ 层级 │ 职责 │ 依赖 │ 示例 │
├────────────────┼──────────────┼────────────────────────────┼─────────────────────────────────────┤
│ Domain │ 核心业务逻辑 │ 无(或只依赖其他领域对象) │ Order, Money, OrderRepository(接口) │
├────────────────┼──────────────┼────────────────────────────┼─────────────────────────────────────┤
│ Application │ 编排业务流程 │ Domain 层 │ CreateOrderUseCase │
├────────────────┼──────────────┼────────────────────────────┼─────────────────────────────────────┤
│ Infrastructure │ 技术实现 │ Domain + Application │ MySQLOrderRepository, StripePayment │
├────────────────┼──────────────┼────────────────────────────┼─────────────────────────────────────┤
│ Presentation │ 用户界面 │ Application 层 │ OrderController, CLI │
└────────────────┴──────────────┴────────────────────────────┴─────────────────────────────────────┘
为什么要这样设计?

  1. 业务逻辑稳定,技术可变

// 场景:从 MySQL 切换到 MongoDB

// ❌ 传统方式:业务代码大量修改
class Order {
public function save(): void {
$this->mysqlDb->insert(…); // 需要改成 $this->mongoDB->insert(…)
}
}

// ✅ DDD 方式:只需更换实现
$orderRepo = new MongoOrderRepository(); // 替换实现
useCase=newCreateOrderUseCase(useCase = new CreateOrderUseCase(useCase=newCreateOrderUseCase(orderRepo); // 业务代码不变!

  1. 易于测试

// 测试时使用内存实现,不需要真实数据库
class InMemoryOrderRepository implements OrderRepository {
private array $orders = [];
public function save(Order $order): void {
$this->orders[] = $order;
}
}

// 测试 Use Case
$useCase = new CreateOrderUseCase(new InMemoryOrderRepository());
useCase−>execute(useCase->execute(useCase>execute(request); // 无需启动 MySQL!

  1. 业务逻辑独立演进

// 领域层修改业务规则,不影响基础设施
class Order {
public function addItem(OrderItem KaTeX parse error: Expected '}', got 'EOF' at end of input: … if (this->items->count() >= 100) {
throw new TooManyItemsException();
}
this−>items−>add(this->items->add(this>items>add(item);
}
}

// Infrastructure 层代码无需修改!

实际示例对比

❌ 违反依赖规则

namespace Domain;
use Illuminate\Database\Eloquent\Model; // ❌ 依赖框架

class Order extends Model { // ❌ 领域对象继承 ORM
protected $table = ‘orders’; // ❌ 领域层知道数据库表名

  public function complete(): void {
      $this->status = 'completed';
      $this->save();  // ❌ 领域逻辑直接保存到数据库
  }

}

问题:

  • 领域层依赖 Laravel 框架(无法脱离框架运行)
  • 业务逻辑和持久化混在一起
  • 无法切换 ORM 或数据库
  • 测试需要真实数据库

✅ 遵循依赖规则

// Domain 层:纯业务逻辑
namespace Domain;

class Order { // ✅ 纯 PHP 对象,无外部依赖
private OrderId $orderId;
private OrderStatus $status;
private array $domainEvents = [];

  public function complete(): void {
      if (!$this->canBeCompleted()) {
          throw new CannotCompleteOrderException();
      }
      $this->status = OrderStatus::COMPLETED;
      $this->domainEvents[] = new OrderCompletedEvent($this->orderId);
  }

}

// Domain 层:定义接口
interface OrderRepository {
public function save(Order $order): void;
}

// Infrastructure 层:实现接口
namespace Infrastructure;

class EloquentOrderRepository implements OrderRepository {
public function save(Order $order): void {
// 使用 Eloquent 持久化
OrderModel::updateOrCreate([
‘order_id’ => $order->getId()->toString(),
], [
‘status’ => $order->getStatus()->getValue(),
]);
}
}

优势:

  • Domain 层可以独立测试(无需框架)
  • 可以切换到 Doctrine/原生 SQL
  • 业务逻辑清晰、可复用

依赖流向图

┌────────────────────────────────────────┐
│ OrderController (Presentation) │
│ 依赖 ↓ │
├────────────────────────────────────────┤
│ CreateOrderUseCase (Application) │
│ 依赖 ↓ │
├────────────────────────────────────────┤
│ Order (Domain) │
│ OrderRepository (接口定义在 Domain) │
│ ↑ 实现 │
├────────────────────────────────────────┤
│ MySQLOrderRepository (Infrastructure) │
│ (依赖 Domain 层的接口) │
└────────────────────────────────────────┘

总结
┌──────────┬────────────────────┬──────────────────┐
│ 方面 │ 依赖规则要求 │ 好处 │
├──────────┼────────────────────┼──────────────────┤
│ 依赖方向 │ 外层 → 内层 │ 业务核心稳定 │
├──────────┼────────────────────┼──────────────────┤
│ 接口定义 │ 内层定义,外层实现 │ 依赖倒置 │
├──────────┼────────────────────┼──────────────────┤
│ 领域层 │ 不依赖任何外层 │ 可独立测试、迁移 │
├──────────┼────────────────────┼──────────────────┤
│ 基础设施 │ 实现领域层接口 │ 技术可替换 │
└──────────┴────────────────────┴──────────────────┘
核心:业务逻辑(Domain)是系统的心脏,技术实现(Infrastructure)是可替换的器官。通过接口(抽象)连接,实现业务与技术的解耦。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐