前回までの振り返り
第三回です。前回までは、MVCのコードを写経して、疑問点を解消していきました。少しは設計についての基礎が身について来た気がします!
今回は、ドメイン駆動設計のコードを写経して見たいと思います。
写経したコード
こちらのサンプルコードをお借りしました。
app
├── Console
├── Domain
│ ├── Models
│ │ └── User.php
│ ├── Repositories
│ │ └── UserRepositoryInterface.php
│ └── Services
│ └── UserService.php
├── Exceptions
├── Http
│ ├── Controllers
│ │ └── UserController.php
│ ├── Middleware
│ └── Requests
├── Infrastructure
│ └── Eloquent
│ ├── Models
│ │ └── EloquentUser.php
│ └── Repositories
│ └── EloquentUserRepository.php
└── Providers
└── AppServiceProvider.php
第一回の時と同様、ユーザー情報を取得する機能を実装していますが、構成は大きく違います。
まずは、構成の特徴を確認しながら、ドメイン駆動設計とはなんなのかを学んできます。
ドメイン駆動設計の特徴
今回写経したコードは、大まかに上の画像のような構成になっています。
各層の役割はざっくり説明すると以下のような感じです。
- プレゼンテーション層:主にユーザーに対するインターフェースを提供する。
- ドメイン層:Presentation層で渡されたデータに対して、ビジネスロジックに基づいて計算等の処理を行う。
- インフラ層:データベースに接続してデータの取得、更新等を行う。
大きな特徴は、名前の通りドメイン層です。ドメイン駆動設計の肝と言い換えて問題ありません。
この場合の"ドメイン"とは、簡単に言うと「システムを作成する対象となる業務領域」を指しており、システムが扱う業務仕様やビジネスルールを設計にそのまま反映させることが、ドメイン駆動設計と言えます。
では、具体的にどのようにして業務仕様を反映させていくのか、ドメイン層の中身を見ていきます。
ドメイン層の内容
ドメイン層には、大きく分けて二種類のファイルがを用いて、ビジネスロジックを適用していきます。
- ドメインオブジェクト
- Model
- ドメインオブジェクトを使用するもの
- Repositry
- Service
ドメインオブジェクトとは
業務上のビジネスルールが落とし込まれたオブジェクトです。
構成する要素は
- 業務に関するデータ(ユーザー名、生年月日など)
- 業務に関するデータを操作、加工する計算ロジック(生年月日から年齢を算出するなど)
<?php
namespace App\Domain\Models;
class User
{
public int $id;
public string $name;
public string $birthday;
public function __construct(int $id, string $name, string $birthday)
{
$this->id = $id;
$this->name = $name;
$this->birthday = $birthday;
}
public function calcAge()
{
this->birthdayを用いて年齢を計算
}
}
特徴は、普通のデータクラスと違い、計算ロジックも合わせて実装している点です。こうすることで、ビジネスルールに変更が起こった際に、変更箇所がドメインオブジェクトだけになるため影響を最小限にすることができます。
ドメインオブジェクトを使用するもの
リポジトリ
実装する場合はインターフェースをドメイン層に実装し、処理本体はインフラ層に書く(ユーザーidでユーザを検索するなど)
<?php
namespace App\Domain\Repositories;
use App\Domain\Models\User;
interface UserRepositoryInterface
{
public function find(int $id): ?User;
}
<?php
namespace App\Infrastructure\Eloquent\Repositories;
use App\Domain\Models\User;
use App\Domain\Repositories\UserRepositoryInterface;
use App\Infrastructure\Eloquent\Models\EloquentUser;
class EloquentUserRepository implements UserRepositoryInterface
{
public function find(int $id): ?User
{
$eloquentUser = EloquentUser::find($id);
if ($eloquentUser === null) {
return null;
}
return new User(
$eloquentUser->id,
$eloquentUser->name,
$eloquentUser->birthday
);
}
}
ドメインサービス
ドメインオブジェクトとして表現すると無理があるもの
極力実装しないことが推奨されている
まとめ
ドメイン駆動設計とは、システムが扱う業務仕様やビジネスルールを設計にそのまま反映させることを目的とした設計方法で、ドメイン層を用いることで、実現させています。
ドメイン層の要素
- ドメインオブジェクト: 業務に関するデータとそのデータに対しての計算ロジック(ビジネスルール)を持つ
- リポジトリ: インフラ層からデータを取得したり、保存したりする
- ドメインサービス : ドメインオブジェクトに記述するべきでない計算ロジック(非推奨)
ということで、ドメイン駆動設計の基本的な部分は押さえることができたかと思います。
※今回勉強したドメイン駆動設計は一般的な例であり、細部が微妙に異なるものが色々あります。
次回は、今回勉強して出た疑問点「インフラ層のModelとドメイン層のModelを分ける意味はあるのか」について考えていきたいと思います。