当前位置:首页>php>使用聚合设计干净的复杂 PHP 应用程序

使用聚合设计干净的复杂 PHP 应用程序

  • 2026-09-05 13:22:40
使用聚合设计干净的复杂 PHP 应用程序

领域驱动设计聚合如何帮助您在现代 PHP 中构建可维护、一致且可读的系统。

现代 PHP 应用程序不再是简单的 CRUD 系统。今天,我们构建分布式系统、微服务、SaaS 平台、事件驱动系统以及领域丰富的应用程序。随着复杂性的增加,传统的 MVC 结构往往在纠缠的业务逻辑、脆弱的模型和不一致的状态管理下崩溃。

一种显著改善复杂系统设计架构概念是聚合,这是领域驱动设计 (DDD) 的核心模式。

在本文中,我们将探讨:

  • 什么是聚合
  • 为什么它们在复杂应用程序中至关重要
  • 它们如何改善可维护性和可读性
  • 如何在现代 PHP (8.0 → 8.5) 中实现聚合
  • 围绕聚合的真实世界 PHP 设计模式

本文假设您已经具备面向对象编程 (OOP)、架构模式以及现代 PHP 特性的经验。

问题:大型 PHP 应用程序中的复杂性

大多数 PHP 应用程序从简单开始。

典型结构:

Controller ├── Service │     └── Repository └── Model (Entity)

但几年后,业务逻辑散布到各处:

  • 验证在控制器中
  • 计算在服务中
  • 状态变更在仓库中
  • 业务规则在数据库触发器中

最终您会得到:

*贫血模型*重复的业务规则*不一致的领域状态*难以测试的逻辑

这正是领域驱动设计 (DDD) 试图解决的问题。

DDD 专注于围绕业务领域建模软件,将代码结构与业务概念和规则对齐。

这种方法中最强大的工具之一是聚合。

什么是聚合?

聚合 是领域对象(实体和值对象)的一个集群,被视为单个单元进行数据修改。

在聚合内部:

  • 有一个根实体
  • 所有操作都通过该根进行
  • 根强制执行业务规则(不变量)

示例:

Order (Aggregate Root) ├── OrderItem ├── OrderItem └── ShippingAddress (Value Object)

外部代码仅与聚合根交互。

$order->addItem($productId, 2);$order->cancel();$order->changeShippingAddress($address);

其余对象是内部实现细节。

这种设计确保业务规则始终被保留。

聚合的核心组件

聚合由几个领域构建块构成。

1. 实体

由身份定义的对象。

示例:

finalclassOrder{publicfunction__construct(        private OrderId $id    ){}}

即使属性发生变化,实体仍然是相同的。

2. 值对象

由属性而非身份定义的对象。

它们通常是不可变的。

示例:

  • Money
  • Email
  • Address
  • DateRange
final readonly classMoney{publicfunction__construct(        public int $amount,        public string $currency    ){}}

3. 聚合根

修改聚合内部对象的唯一入口点。

示例:

finalclassOrder{privatearray $items = [];publicfunctionaddItem(ProductId $productId, int $quantity): void{if ($quantity <= 0) {thrownew DomainException("Invalid quantity");        }$this->items[] = new OrderItem($productId, $quantity);    }}

外部服务不能直接修改 OrderItem。

为什么聚合在复杂系统中重要

聚合解决了几个架构问题。

1. 保护业务不变量

不变量是必须始终为真的规则。

示例:

订单总额必须等于项目总和

没有聚合时:

Service A 修改订单项目Service B 重新计算总额Service C 更新折扣

系统很容易变得不一致。

使用聚合时:

$order->addItem(...)

根保证不变量。

示例:

publicfunctionaddItem(ProductId $productId, int $quantity): void{$this->items[] = new OrderItem($productId, $quantity);$this->recalculateTotal();}

一致性得到保证。

2. 定义事务边界

聚合定义事务边界。

聚合内部的所有变更必须一起成功或失败。

示例:

Order ├── items ├── shipping └── payment status

要么所有变更成功,要么整个操作回滚。

这大大简化了事务管理。

3. 减少耦合

没有聚合时:

OrderItem -> ProductProduct -> InventoryInventory -> Order

您会得到意大利面条般的依赖图。

聚合限制引用。

外部对象只能引用聚合根。

这大大减少了耦合。

4. 封装领域逻辑

聚合强制执行行为驱动模型。

而不是:

$order->status = "shipped";

您使用:

$order->ship();

这确保规则被强制执行。

5. 改善可读性

比较两种方法。

没有聚合

$orderService->addItem(...)$orderRepository->update(...)$totalService->recalculate(...)

使用聚合

$order->addItem(...)$orderRepository->save($order)

业务流程变得更容易理解。

使用现代 PHP (8.0 → 8.5) 的聚合

现代 PHP 特性使聚合的实现显著更容易。

让我们探讨最有用的特性。

1. 构造函数属性提升

减少样板代码。

finalclassOrderItem{publicfunction__construct(        private ProductId $productId,        private int $quantity    ){}}

2. 只读属性

完美适用于值对象。

final readonly classAddress{publicfunction__construct(        public string $street,        public string $city    ){}}

保证不可变性。

3. 用于领域状态的枚举

enum OrderStatus{case Draft;case Paid;case Shipped;case Cancelled;}

在聚合中使用。

private OrderStatus $status;

4. 类型化属性

避免运行时错误。

privatearray $items = [];

更好:

/** @var OrderItem[] */privatearray $items = [];

5. 属性

对于框架或事件溯源很有用。

#[Aggregate]finalclassOrder{}

示例:在现代 PHP 中的订单聚合

finalclassOrder{privatearray $items = [];private OrderStatus $status;publicfunction__construct(        private OrderId $id    ){$this->status = OrderStatus::Draft;    }publicfunctionaddItem(ProductId $productId, int $quantity): void{if ($this->status !== OrderStatus::Draft) {thrownew DomainException("Cannot modify finalized order");        }$this->items[] = new OrderItem($productId, $quantity);    }publicfunctionpay(): void{if (empty($this->items)) {thrownew DomainException("Order must contain items");        }$this->status = OrderStatus::Paid;    }publicfunctionship(): void{if ($this->status !== OrderStatus::Paid) {thrownew DomainException("Order must be paid first");        }$this->status = OrderStatus::Shipped;    }}

注意:

  • 状态转换受控
  • 规则被强制执行
  • 外部代码无法破坏状态

聚合的仓库模式

聚合通常通过仓库持久化。

示例:

interfaceOrderRepository{publicfunctionsave(Order $order): void;publicfunctionfind(OrderId $id): ?Order;}

仓库将聚合视为集合。

这将持久化复杂性从领域中隐藏。

常见的聚合错误

1. 巨大的聚合

不良:

User ├── Orders ├── Payments ├── Addresses ├── Notifications

太大 → 性能问题。

2. 贫血聚合

不良:

$order->setStatus("paid");

没有行为。

3. 跨聚合引用

而不是:

Order -> Product object

使用:

Order -> ProductId

何时应该使用聚合?

聚合最有用时:

  • 必须保留业务规则
  • 操作必须一致
  • 系统具有复杂状态转换
  • 多个实体一起变更

典型领域:

  • 电子商务
  • 金融科技
  • 预订系统
  • 工作流引擎
  • SaaS 平台

对大型 PHP 系统的益处

使用聚合会导致:

更干净的架构

业务逻辑位于领域模型内部。

更好的可维护性

规则被集中化。

改进的可测试性

聚合易于单元测试。

减少错误

无效状态变得不可能。

总结

聚合不仅仅是领域驱动设计中的理论概念。

它们是构建大型、可维护 PHP 应用程序的最实用工具之一。

通过强制执行:

  • 清晰边界
  • 封装行为
  • 事务一致性

聚合允许开发者编写干净、可读且可靠的领域模型。

结合现代 PHP 特性(只读、枚举、属性、强类型),聚合成为建模复杂系统的一种强大方式。

对于构建长期产品的团队,采用聚合往往标志着从框架驱动开发到架构驱动系统的转变。

而那就是真正可扩展性的开始。

最新文章

随机文章