3
4

More than 1 year has passed since last update.

「ドメイン駆動設計入門」のLaravelによる実装例:Chapter 3-ライフサイクルのあるオブジェクト「エンティティ」

Last updated at Posted at 2023-06-11

本プロジェクトでは、成瀬允宣氏著の「ドメイン駆動設計入門」のLaravel 10を用いた実装例を示します。

当該書籍では、ドメイン駆動設計(Domain Driven Deplopment, DDD)を用いたWEBアプリケーションの開発例が示されており、素晴らしい著作であることは言うまでもありません。
しかしながら実装はC#で示されているため
、例えばPHPフレームワークの代表例であるLaravelなどでDDDによる実装を行いたい場合には、具体的な実装例が分かりづらくなってしまいます。

そこで本プロジェクトでは、「ドメイン駆動設計入門」のLaravelによる実装例を示すことで、Laravel開発者のDDD実装の手助けとなることを目指します。あくまでLaravelの実装例を示すことを目的としていますので、ドメイン駆動設計とは何かについては書籍をお読みください。

これが皆様の開発の手助けとなれば幸いです。ご意見等ございましたら、コメント欄やIssueでお伝えいただければと思います。

目次

Githubにソースコードはアップ済みです。

Chapter 3: ライフサイクルのあるオブジェクト「エンティティ」

Docker環境で開発

あらかじめ、srcフォルダをVisual Studio Codeで開き、Command PaletteのDev Containers: Reopen in ContainerでDocker環境に入っておきます。

Laravelにおけるエンティティの作成方針

書籍によれば、エンティティの性質は以下の通りです。

  • 可変である
  • 同じ属性であっても区別される
  • 同一性により区別される

また、ライフサイクルのあるオブジェクトという説明もあります。
これらの意味で、エンティティは値オブジェクトと区別されます。

Laravelでは、Eloquentが採用されています。
これにより、エンティティクラスでデータベースに関する操作も定義され、面倒なデータベース操作が楽になります。
従って、Eloquentを使用して基本に忠実にエンティティを実装すれば良いです。

Userエンティティの作成

書籍Chapter6で使用されるリスト6.1のUserクラスを作成していきます。

あらかじめ、app/models/User.phpと、database/migrations/*_create_user_table.phpを削除しておきます。
Userクラスとデータベースのtableのスキーマを作成しましょう。

php artisan make:model User --migration

app/Models/User.phpdatabase/migrations/2023_06_25_033245_create_users_table.phpが作成されたと思います。
後者のファイル名には作成日が反映されているはずです。
User.phpではUserクラスを記述しますが、*_create_users_table.phpでは、データベースのスキーマを定義します。
まずは、User.phpから実装し、その後に*_create_users_table.phpを記述していきましょう。

Userエンティティの実装

Userエンティティの実装は以下のようになります。

app/Models/User.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

use App\Casts\UserName as UserNameCast;

class User extends Model
{
    use HasFactory;
    use HasUuids;

    protected $fillable = [
        'username'
    ];

    protected $casts = [
        'username' => UserNameCast::class
    ];
}

Chapter 2では、UserIdの値オブジェクトを作成しましたが、きちんと独自のIdを作成するのは大変ですので、ここではLaravel built-inのUUIDを使いましょう。
LaravelのEloquentモデルでは、デフォルトではint型のidが使用され、自動でインクリメントされるようになっています。
代わりにstring型のUUIDを使用したい場合には、use HasUuids;をクラス内に記述することで、UUIDが使用できるようになります。
とても便利ですね。

次に、Chapter 2で作成したUserName値オブジェクトをUserクラスがアトリビュートとして持てるようにします。
Laravelでは、fillableにModelが持つプロパティを記述していきます。
ここでは、UserエンティティはUserName値オブジェクトを持つので、fillableusernameを追加します。
ここでusernameはDBのカラム名に対応します。
usernameはUserName値オブジェクトとして渡されて欲しいので、Chapter 2で作成したUserNameクラスをcastsに渡します。

なお、Laravelではfillableの他にguardedもあります。
fillableに指定したアトリビュートは、後述のcreateメソッドなどで指定した際にオブジェクトに反映されますが、guardedに指定したものは無視され、オブジェクトに反映されません。
デフォルトではguareded=['*']となっており、全てがguardedに指定されます。
従って、idguardedとして指定され、createなどで任意に代入できないようになっているので、そのままにしておきます。

schemaの作成

以下のようにschemaを作成します。

database/migrations/2023_06_25_033245_create_users_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->uuid('id')->primary();
            $table->string('username')->unique();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
    }
};

前節でidとしてUUIDを使用することにしたので、その部分の指定を行っています。
また、usernameをstring型として保存するカラムも指定しています。
書籍では、usernameをuniqueなもの仮定しているので、DBの方でもそのように指定しておきます。

動作確認

ちゃんとしたテストの作成はChapter 5で出てきますので、ここでは簡単に動作確認をしてみます。

まず、DBのschemaに変更を加えたので、それを反映させます。

php artisan migrate:fresh

以下で、usersのtableがどうなっているか確認してみましょう。

php artisan db:table users
出力
users ..............................................................................................  
Columns .......................................................................................... 4  
Size ....................................................................................... 0.02MiB  

Column ........................................................................................ Type  
id string ................................................................................... string  
username string ............................................................................. string  
created_at datetime, nullable ............................................................. datetime  
updated_at datetime, nullable ............................................................. datetime  

Index ..............................................................................................  
PRIMARY id ......................................................................... unique, primary  
users_username_unique username .............................................................. unique 

上記から、idとusrnameカラムが問題なく作成されていることが分かります。
親切にも、created_atとupdated_atのカラムも作成されています。
これらは一般的なDBの使用で有意義なものですので、そのまま残しておきましょう。

web.phpを以下のように変更し、簡単な動作確認をしてみます。

routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\ValueObjects\UserName;
use App\Models\User;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/

Route::get('/', function () {
    $username = new UserName("ReiRev");
    $user = User::create(
        [
            "username" => $username,
            'id' => 'hoge'
        ]
    );
    $users = User::all();
    foreach ($users as $user) {
        dump($user);
    }
    return view('welcome');
});

php artisan serveでサーバーを起動し、localhostを開くと、問題なくUserが作成されていることが確認できると思います。
ここで、idも指定してcreateしていますが、実際のidはUUIDになっているはずです。
これは後述した通り、idがguardedになっており、createでは代入できるようになっているからです。
ブラウザをリロードして見ると、以下のようにエラーが表示され、usernameがuniqueなカラムになっていることが確認できます。

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'ReiRev' for key 'users.users_username_unique' 

まとめ

本記事では、LaravelのEloquentを使用してエンティティの作成を行いました。
Eloquentを用いることで、少ないコード量で実装を行うことができますので、有効活用していきましょう。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4