0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

「ドメイン駆動設計入門」を読んで_その5

Posted at

この記事は

「ドメイン駆動設計入門」を読んでまとめている第5弾

前回記事はこちら

今回はアプリケーションの組み立て集約についてまとめる

アプリケーションを組み立てる

ドメインオブジェクト、サービス、リポジトリ、ファクトリなど、アプリケーションを組み立てるために必要な部品の説明が終わった

それらの部品を使ってアプリケーションを組み立てる手順は以下のとおり

  1. 要求にしたがって必要な機能を洗い出す
  2. 機能を成り立たせるために必要なユースケースを洗い出す
    1. 機能を実現するためには単一のユースケースではなく、ユースケースの組み合わせが必要なときもある
  3. アプリケーションにとって必要な知識・ルールを選び、ドメインオブジェクトをつくる
  4. ユースケースを実現するために、ドメインオブジェクトを用いてアプリケーションサービスをつくる

サークル機能を実現する

手順 2 に書いたように、ある機能を実現するためのユースケースが1つとは限らない
「サークル機能」を実現するにはいくつかのユースケースが考えられる

さらにサークルには、人数の上限やすでに存在するサークルと同じ名前が使えない、など制約・ルールが存在する

もう少し詳細な手順

機能はトップダウンで洗い出し、実装はボトムアップで組み立てていく

参考になる階層分け

  1. ルールを持ったドメインオブジェクト(エンティティ値オブジェクト)をつくる
    2. ルールが肥大化する場合は仕様という考え方をつかう(次記事)
  2. 永続化を担うリポジトリをつくる
  3. オブジェクト生成を担うファクトリをつくる
  4. ドメインオブジェクトが持つと不自然になるふるまい(例:重複確認)をドメインサービスに切り出す
  5. ユースケースを組み立てる
    1. コマンドクラスをつくる
  6. アプリケーションサービスをつくる

ルールが漏れ出すと・・・

知識やルールがドメインオブジェクトから漏れ出してプロダクトに点在すると
ルール改正のたびに修正漏れのリスクが生じる

対策として集約という考え方がある

集約

データを変更するための単位としてあつかわれるオブジェクトの集まりを集約といいます(P267)

集約には境界とルートが存在する。外部から集約に対する変更はすべてルートを通しておこなわれる。集約の外部から境界内部のオブジェクトを操作してはいけない。

UserNameを操作できるのは、ユーザ集約のルートとなるUserのみ

user.Name = "Taro"      // Bad !! 直接操作してはいけない
user.ChangeName("Taro") // Good!! メソッドをつかえば、NULL検証などルールと照らし合わせてから変更を受け入れることができる

circle.Members.Add(user) // Bad !! Cicle内部のメンバーリストは公開しないほうがいい
circle.Join(user)        // Good !! こちらの方が文脈的にも自然

CircleMemberリストは外部に公開せず、privateにしておくとよい

デメテルの法則

集約をチェックするための1ツールがデメテルの法則

簡潔に言うと「直接の友達とだけ話すこと」と要約できる (Wikipedia)

CircleUserのことだけ知っている

直接ユーザーの名前プロパティにアクセスするのではなく、「名前を変更してね」とだけお願いするとよい

// 悪い例)ユーザー側でルールを記述している
if(circle.Members.Count > N)
{
    // ...
}

// よい例)ルールをサークルクラスに問い合わせている
if(circle.isFull())
{
    // ...
}

集約をどう区切るか

UserCircleは変更の単位で区切られている
裏を返せばCircleUserを変更してはいけない

もし変更して面倒をみようものなら、サークルリポジトリにユーザ情報変更のロジックが追加(汚染)されてしまう。
当たり前であるが、すでにユーザリポジトリにはユーザ情報変更のロジックがあるため、同じロジックが重複して点在してしまう

ロジックの重複・点在は、後々の修正時に修正漏れを引き起こす・・・

IDによるコンポジション

しかしながら、CircleはメンバーリストとしてUserインスタンスを保持しているので、user.ChangeName("Taro")を呼び出せてしまう

どうすればいいか?

CircleUserはエンティティであるため、IDで識別できる
つまりインスタンス全体を知らなくとも、IDだけでインスタンスを識別できる
IDだけであれば、ほかのメソッドは呼び出されずに済む

class Circle
{
    public List<User> Members {get; set;}
    // ユーザクラスをつかうと、余計なメソッドを呼ばれる心配がある
    
    // ↓↓↓ 変更 ↓↓↓
    
    public List<UserId> Membsers {get; set;}
    // ユーザIDだけつかうことで、情報を隠ぺいできる。むやみにメソッドが呼ばれない
    // メモリや再構築の手間も省ける
}

集約の大きさ

集約が変更されるたびにトランザクションがおきる
集約が大きくなると、トランザクションが失敗する可能性も大きくなる
トランザクションが失敗し、リトライ頻度が大きくなるとアプリケーションの質が低下する

大きくなりすぎた集約は境界を見直すべき

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?