PHP 怎么领域模型

wen PHP项目 2

本文目录导读:

PHP 怎么领域模型

  1. 什么是领域模型
  2. 基础领域模型示例
  3. 聚合根(Aggregate Root)
  4. 仓储模式(Repository Pattern)
  5. 领域服务(Domain Service)
  6. 领域事件(Domain Events)
  7. 应用层(Application Layer)
  8. 最佳实践

我来详细讲解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

关键设计原则

  1. 实体有ID,值对象没有
  2. 业务规则在实体内部,不在服务层
  3. 仓储只负责持久化,不处理业务逻辑
  4. 通过领域事件解耦不同模块
  5. 应用层作为薄层,不包含业务逻辑

领域模型模式让业务代码更清晰、可维护,但也需要适度使用,避免过度设计,对于简单项目,使用传统的ActiveRecord模式可能更加合适。

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