本文目录导读:

- 什么是领域模型
- 基础领域模型示例
- 聚合根(Aggregate Root)
- 仓储模式(Repository Pattern)
- 领域服务(Domain Service)
- 领域事件(Domain Events)
- 应用层(Application Layer)
- 最佳实践
我来详细讲解PHP中领域模型的实现方法。
什么是领域模型
领域模型是DDD(领域驱动设计)中的核心概念,它将业务逻辑和规则封装在模型对象中,而不是散落在服务层或控制器中。
基础领域模型示例
实体(Entity)
<?php
class Order
{
private string $id;
private string $customerId;
private array $items = [];
private string $status;
private float $totalAmount;
private \DateTimeImmutable $createdAt;
public function __construct(string $customerId, string $id = null)
{
$this->id = $id ?? uniqid('order_');
$this->customerId = $customerId;
$this->status = 'pending';
$this->createdAt = new \DateTimeImmutable();
}
public function addItem(OrderItem $item): void
{
// 业务规则:订单已支付不能再添加商品
if ($this->status === 'paid') {
throw new \DomainException('已支付的订单不能添加商品');
}
$this->items[] = $item;
$this->recalculateTotal();
}
public function removeItem(string $itemId): void
{
// 业务规则验证
if ($this->status === 'paid') {
throw new \DomainException('已支付的订单不能移除商品');
}
$this->items = array_filter(
$this->items,
fn(OrderItem $item) => $item->getId() !== $itemId
);
$this->recalculateTotal();
}
public function checkout(): void
{
if (empty($this->items)) {
throw new \DomainException('订单没有商品不能结算');
}
if ($this->status !== 'pending') {
throw new \DomainException('订单状态不正确');
}
$this->status = 'paid';
$this->processPayment();
}
private function recalculateTotal(): void
{
$this->totalAmount = array_sum(
array_map(fn(OrderItem $item) => $item->getSubtotal(), $this->items)
);
}
private function processPayment(): void
{
// 支付逻辑
}
// Getters...
}
值对象(Value Object)
<?php
class Money
{
private float $amount;
private string $currency;
public function __construct(float $amount, string $currency)
{
if ($amount < 0) {
throw new \InvalidArgumentException('金额不能为负数');
}
$this->amount = $amount;
$this->currency = $currency;
}
public function add(Money $other): Money
{
if ($this->currency !== $other->currency) {
throw new \DomainException('货币类型不一致');
}
return new Money(
$this->amount + $other->amount,
$this->currency
);
}
public function multiply(float $factor): Money
{
return new Money($this->amount * $factor, $this->currency);
}
// Getters
public function getAmount(): float { return $this->amount; }
public function getCurrency(): string { return $this->currency; }
// 值对象应该有equals方法
public function equals(Money $other): bool
{
return $this->amount === $other->amount
&& $this->currency === $other->currency;
}
}
// 更复杂的值对象
class Address
{
private string $street;
private string $city;
private string $country;
private string $postalCode;
public function __construct(string $street, string $city, string $country, string $postalCode)
{
$this->street = $street;
$this->city = $city;
$this->country = $country;
$this->postalCode = $postalCode;
}
public function format(): string
{
return sprintf('%s, %s, %s %s',
$this->street,
$this->city,
$this->country,
$this->postalCode
);
}
}
聚合根(Aggregate Root)
<?php
class Customer implements AggregateRoot
{
private string $id;
private string $name;
private string $email;
private array $addresses = [];
private array $orders = [];
public function __construct(string $name, string $email)
{
$this->id = uniqid('cust_');
$this->name = $name;
$this->email = $email;
}
public function placeOrder(Order $order): void
{
if ($order->getCustomerId() !== $this->id) {
throw new \DomainException('订单不属于该客户');
}
$this->orders[] = $order;
$order->setCustomer($this);
// 触发领域事件
$this->raiseEvent(new OrderPlaced($order));
}
public function addAddress(Address $address): void
{
if (count($this->addresses) >= 5) {
throw new \DomainException('地址数量不能超过5个');
}
$this->addresses[] = $address;
}
public function changeEmail(string $newEmail): void
{
if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('邮箱格式不正确');
}
$this->email = $newEmail;
}
// 领域事件处理
private array $events = [];
protected function raiseEvent(object $event): void
{
$this->events[] = $event;
}
public function releaseEvents(): array
{
$events = $this->events;
$this->events = [];
return $events;
}
}
// 聚合根接口
interface AggregateRoot
{
public function releaseEvents(): array;
}
仓储模式(Repository Pattern)
<?php
interface OrderRepository
{
public function find(string $id): ?Order;
public function save(Order $order): void;
public function delete(Order $order): void;
public function findByCustomer(string $customerId): array;
}
// 数据库实现
class MysqlOrderRepository implements OrderRepository
{
private PDO $db;
private EventDispatcher $eventDispatcher;
public function __construct(PDO $db, EventDispatcher $eventDispatcher)
{
$this->db = $db;
$this->eventDispatcher = $eventDispatcher;
}
public function find(string $id): ?Order
{
// 数据映射逻辑
$stmt = $this->db->prepare('SELECT * FROM orders WHERE id = ?');
$stmt->execute([$id]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$data) {
return null;
}
return $this->mapToOrder($data);
}
public function save(Order $order): void
{
// 保存订单
$stmt = $this->db->prepare(
'INSERT INTO orders (id, customer_id, status, total_amount, created_at)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE status = ?, total_amount = ?'
);
$stmt->execute([
$order->getId(),
$order->getCustomerId(),
$order->getStatus(),
$order->getTotalAmount(),
$order->getCreatedAt()->format('Y-m-d H:i:s'),
$order->getStatus(),
$order->getTotalAmount()
]);
// 保存订单项
$this->saveItems($order);
// 发送领域事件
foreach ($order->releaseEvents() as $event) {
$this->eventDispatcher->dispatch($event);
}
}
public function delete(Order $order): void
{
$stmt = $this->db->prepare('DELETE FROM orders WHERE id = ?');
$stmt->execute([$order->getId()]);
}
public function findByCustomer(string $customerId): array
{
// 查询逻辑
return [];
}
private function mapToOrder(array $data): Order
{
$order = new Order($data['customer_id'], $data['id']);
// 设置其他属性...
return $order;
}
private function saveItems(Order $order): void
{
// 保存订单项逻辑
}
}
领域服务(Domain Service)
<?php
class OrderService
{
private OrderRepository $orderRepository;
private CustomerRepository $customerRepository;
private PricingService $pricingService;
public function __construct(
OrderRepository $orderRepository,
CustomerRepository $customerRepository,
PricingService $pricingService
) {
$this->orderRepository = $orderRepository;
$this->customerRepository = $customerRepository;
$this->pricingService = $pricingService;
}
public function createOrder(string $customerId, array $items): Order
{
$customer = $this->customerRepository->find($customerId);
if (!$customer) {
throw new \DomainException('客户不存在');
}
$order = new Order($customerId);
foreach ($items as $item) {
$order->addItem($this->createOrderItem($item));
}
$this->applyDiscount($order, $customer);
$this->orderRepository->save($order);
return $order;
}
private function createOrderItem(array $data): OrderItem
{
$price = $this->pricingService->calculatePrice(
$data['product_id'],
$data['quantity']
);
return new OrderItem(
$data['product_id'],
$data['quantity'],
$price
);
}
private function applyDiscount(Order $order, Customer $customer): void
{
if ($customer->isVip()) {
$discount = $order->getTotalAmount() * 0.1;
$order->applyDiscount($discount);
}
}
}
领域事件(Domain Events)
<?php
// 事件定义
class OrderPlaced
{
public function __construct(
public readonly string $orderId,
public readonly string $customerId,
public readonly float $totalAmount,
public readonly \DateTimeImmutable $occurredAt = new \DateTimeImmutable()
) {}
}
class OrderPaid
{
public function __construct(
public readonly string $orderId,
public readonly float $amountPaid,
public readonly \DateTimeImmutable $occurredAt = new \DateTimeImmutable()
) {}
}
// 事件分发器
class EventDispatcher
{
private array $listeners = [];
public function listen(string $eventClass, callable $listener): void
{
$this->listeners[$eventClass][] = $listener;
}
public function dispatch(object $event): void
{
$eventClass = get_class($event);
foreach ($this->listeners[$eventClass] ?? [] as $listener) {
$listener($event);
}
}
}
// 事件处理器
class OrderEventHandler
{
public function handleOrderPlaced(OrderPlaced $event): void
{
// 发送通知
$this->sendEmail($event->customerId, '订单已创建');
// 更新库存
$this->updateInventory($event->orderId);
}
public function handleOrderPaid(OrderPaid $event): void
{
// 生成发票
$this->generateInvoice($event->orderId);
// 更新客户累计消费
$this->updateCustomerSpending($event->customerId, $event->amountPaid);
}
// 其他处理方法...
}
应用层(Application Layer)
<?php
class OrderApplication
{
private OrderService $orderService;
private OrderRepository $orderRepository;
private EventDispatcher $eventDispatcher;
public function __construct(
OrderService $orderService,
OrderRepository $orderRepository,
EventDispatcher $eventDispatcher
) {
$this->orderService = $orderService;
$this->orderRepository = $orderRepository;
$this->eventDispatcher = $eventDispatcher;
}
public function placeOrder(array $request): array
{
try {
$order = $this->orderService->createOrder(
$request['customer_id'],
$request['items']
);
return [
'success' => true,
'order_id' => $order->getId()
];
} catch (\DomainException $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
public function getOrderDetail(string $orderId): array
{
$order = $this->orderRepository->find($orderId);
if (!$order) {
throw new \RuntimeException('订单不存在');
}
return $this->serializeOrder($order);
}
private function serializeOrder(Order $order): array
{
return [
'id' => $order->getId(),
'items' => array_map(
fn($item) => [
'product_id' => $item->getProductId(),
'quantity' => $item->getQuantity(),
'price' => $item->getPrice()
],
$order->getItems()
),
'total' => $order->getTotalAmount(),
'status' => $order->getStatus()
];
}
}
最佳实践
目录结构
src/
├── Domain/
│ ├── Entity/
│ │ ├── Order.php
│ │ └── Customer.php
│ ├── ValueObject/
│ │ ├── Money.php
│ │ └── Address.php
│ ├── Repository/
│ │ └── OrderRepository.php
│ ├── Service/
│ │ └── OrderService.php
│ └── Event/
│ ├── OrderPlaced.php
│ └── OrderEventHandler.php
├── Infrastructure/
│ └── Persistence/
│ └── MysqlOrderRepository.php
└── Application/
└── OrderApplication.php
关键设计原则
- 实体有ID,值对象没有
- 业务规则在实体内部,不在服务层
- 仓储只负责持久化,不处理业务逻辑
- 通过领域事件解耦不同模块
- 应用层作为薄层,不包含业务逻辑
领域模型模式让业务代码更清晰、可维护,但也需要适度使用,避免过度设计,对于简单项目,使用传统的ActiveRecord模式可能更加合适。