Laravel + Vue.js + DDDのすゝめ – リポジトリを作る

Laravel + Vue.js + DDDのすゝめ – リポジトリを作る

リポジトリとは、データアクセス機能を一元管理することで保守性を向上させ、ドメインとデータアクセスを切り離すことを目的にしたクラスです。
切り離すことでドメインが簡潔になり、ユニットテストもデータソースへのアクセスが別れるので行いやすくなります。

連載

本記事は複数記事の連載記事の1つです。

この記事に関連するコミット

VSCodeにLaravelを入力補完して貰う

Laravelのファサードやモデルは、そのままではVSCodeは入力補完ができません。
「User::」まで入力したら、コールできるメソッド群を表示して貰いたいですが、表示されません。

そこで、「barryvdh/laravel-ide-helper」パッケージをcomposerから入れます。
このパッケージはFacadeや存在しているモデルやデータベースコネクションを使って入力補完用の定義がぎっしり詰まった定義ファイルを作ってくれます。
このファイルが存在しているだけで、VSCodeは入力補完を行えるようになります。

インストール

composer require --dev barryvdh/laravel-ide-helper

ファサードの定義ファイルを作成する

artisanコマンドに追加されるので、以下のコマンドを実行してルートディレクトリに「_ide_helper.php」が作成されればOKです。

php artisan ide-helper:generate

モデルの定義ファイルを作成する

モデルは以下のコマンドを実行して「_ide_helper_models.php」を作成します。

このモデルファイルは、現時点で存在している状態で作成されます。
マイグレーションの追加や変更をした場合は再実行する必要があります。

php artisan ide-helper:model --nowrite

リポジトリを作る

リポジトリはインフラストラクチャ層の「packages/infrastructure/persistance」に配置します。

OrderRepositoryを例に挙げていきます。

<?php

namespace Packages\Infrastructures\Persistance\Orders;

use App\Models\OrderModel;
use Illuminate\Support\LazyCollection;
use Packages\Domains\Orders\Order;

class OrderRepository
{
    public function find(int $orderId): Order
    {
        return OrderModel::find($orderId)->toEntity();
    }

    /**
     * @return LazyCollection<Order>
     */
    public function fetchAll(): LazyCollection
    {
        return OrderModel::cursor()
            ->map(fn (OrderModel $m) => $m->toEntity())
        ;
    }

    /**
     * @return int オートインクリメント採番されたID
     */
    public function insert(Order $order): int
    {
        return $order->toModel()->create()->id;
    }

    /**
     * @return boolean 更新成功:true / 失敗:false
     */
    public function update(Order $order): bool
    {
        return $order->toModel()->save();
    }
}

Eloquentでデータアクセスするため、OrderModelを呼び出します。
当然、データベースから取得されたデータはOrderModelにマッピングされて返されますが、これをそのまま呼び出し元のレイヤーへ返してはいけません。
モデルの記事で述べていたように、EloquentのModelsを継承したOrderModelをアプリケーション層へ返すと何でも出来てしまいます。

そのため、OrderRepositoryへ引数で渡すときはOrderクラス、返却するときもOrderクラスとします。

※DDD本来はレイヤー間を横断しているので、インターフェースに依存させるためにクラスではなくインターフェースを指定しますが、今回は指定していません。

PHPはJavaのJARやC#のDLLといったパッケージ相当の機能が弱く、スコープのinternalなども存在しません。
Composerでレイヤーごとにパッケージを分けられなくはないですが、書き換えるたびに更新が必要で現実的ではなく、internalスコープもないので細かく制御することも出来ないため、PHPらしく緩い感じになっています。

サンプルメソッドなので必ずこれを揃える必要はなく、要件で必要なものだけでOKです。

find

IDを指定して1件を取得します。
ポイントはtoEntityメソッドでModelからEntityに変換して返却しています。

fetchAll

全件を取得します。
Eloquentではcursorメソッドで取得することでクエリが遅延実行されます。
奥が深いのでここでは説明を省きますが、大量件数を取得する場合はgetではなくcursorで取得するとメモリ使用量が節約できます。

また、PHPDocに@return LazyCollection<Order>のようにコレクションと内包する型を記載することで、VSCodeの補完が効きます。

insert

新しいレコードを追加します。
永続化したいデータは必ずOrderクラスを引数にして、そのまま永続化します。
今回はIDをオートインクリメントで振っており、データを追加した時点で初めて採番されるのでIDを返却します。

update

既存のレコードを更新します。
insertと同様にOrderクラスを引数にして、更新します。

実案件ではもっと複雑なクエリが必要なんだけど?

複雑なクエリが必要になるケースは主に参照系がほとんどと言われています。
様々な条件で検索したり、複数のテーブルを結合して一覧を生成するなどが挙げられます。

これらはデータストアの読み取りと更新を分離するCQRSパターンで別途対応します。

プログラミングカテゴリの最新記事