💨

Laravelでドメイン駆動設計(DDD)を実践する具体例

に公開

ドメイン駆動設計(DDD)は、ビジネスロジックを明確にし、保守性を向上させる設計手法です。
LaravelにDDDを適用する際のポイントとして以下の2点を取り入れた具体例を書きます。

  • 小さいクラスと短いメソッドで行動を整理
  • 値オブジェクトやコレクションオブジェクトでロジックを集約して複雑さを隠蔽

ファイル構成

app/
├── Models/
│   ├── Order.php
│   ├── OrderItem.php
│   └── ValueObjects/
│       ├── OrderItemCollection.php
│       ├── Quantity.php
│       └── Money.php

① Orderモデル

class Order extends Model
{
    public function items(): OrderItemCollection
    {
        return new OrderItemCollection($this->orderItems);
    }

    public function totalAmount(): Money
    {
        return $this->items()->totalAmount();
    }
}

利点

  • 短く明確なメソッドで可読性が向上。
  • Orderモデルは注文全体の管理だけに集中し、詳細な計算処理を隠蔽できる。

② OrderItemモデル

class OrderItem extends Model
{
    public function quantity(): Quantity
    {
        return new Quantity($this->quantity);
    }

    public function unitPrice(): Money
    {
        return new Money($this->unit_price);
    }

    public function subtotal(): Money
    {
        return $this->unitPrice()->multiply($this->quantity());
    }
}

利点

  • 各メソッドが明確に値オブジェクトを返し、型の安全性が担保される。
  • ビジネスロジックが簡潔でバグの可能性を軽減。

② 値オブジェクトの例(QuantityとMoney)

Quantity.php

class Quantity
{
    private int $value;

    public function __construct(int $value)
    {
        if ($value <= 0) {
            throw new InvalidArgumentException('数量は1以上です。');
        }
        $this->value = $value;
    }

    public function value(): int
    {
        return $this->value;
    }
}

Money.php

class Money
{
    private int $amount;

    public function __construct(int $amount)
    {
        if ($amount < 0) {
            throw new InvalidArgumentException('金額は0以上である必要があります。');
        }
        $this->amount = $amount;
    }

    public function multiply(Quantity $quantity): Money
    {
        return new Money($this->amount * $quantity->value());
    }
}

利点

  • 制約を設けることで、データが常に有効な状態を保証。
  • 型安全性が向上し、バグや予期しない動作を防ぐ。

③ コレクションオブジェクト(OrderItemCollection)

class OrderItemCollection
{
    private Collection $items;

    public function __construct($items)
    {
        $this->items = collect($items);
    }

    public function totalAmount(): Money
    {
        return $this->items->reduce(
            fn(Money $carry, OrderItem $item) => $carry->add($item->subtotal()),
            new Money(0)
        );
    }
}

利点

  • 複雑なロジックをコレクション内部に集約し、モデルやコントローラーのコードが簡潔に。
  • 再利用性とテスト容易性が向上。

利用例(Controllerなどでの活用)

$order = Order::find($id);
$totalAmount = $order->totalAmount()->amount();

利点

  • クライアントコードはシンプルになり、詳細を隠蔽。
  • 保守性が高く変更にも柔軟に対応可能。

このようにLaravelでDDDを適用するとコードの品質、可読性、保守性が大きく向上します。
本で勉強するとjavaやC#で書かれているので、今回はlaravelで取り入れるとどんな感じになるのか、イメージが湧けばと。実践で活かしていきたい。

Discussion