Open16

IDDD本 読書メモ

moriyuumoriyuu

2章 ドメイン、サブドメイン、境界づけられたコンテキスト

  • 戦略的と戦術的
    • 戦略的:サブドメインや境界づけられたコンテキストの見極め
    • 戦術的:エンティティや値オブジェクトや集約の見極め
  • 問題空間と解決空間
    • 問題空間:戦略的、のほう?
    • 解決空間:戦術的、のほう?コンテキストマップはこちら
  • 基本的にサブドメインと境界づけられたコンテキストは 1:1 になるのが理想らしい
moriyuumoriyuu

3章 コンテキストマップ

  • コンテキストマップ
    • ACL (AntiCorruption Layer):腐敗防止層 = 使う側のコンテキストが使うコンテキストのやつをラップする層
    • OHS (Open Host Service):公開ホストサービス = REST API とかで公開されてるやつ
    • PL (Published Language):公表された言語 = たぶん外部コンテキストへ public なユビキタス言語のこと
  • 「たとえば、コラボレーションコンテキストが認証・アクセスコンテキストに対して、モデレーター権限を持つユーザーのリソースを問い合わせるという操作が、その一例だ。リクエストしたリソースを、XMLやJSONなどで受け取るかもしれない。それを、値オブジェクトであるModeratorに変換する。Moderatorのインスタンスは下流のモデルの用語を反映するものであり、上流のモデルとは関係がない。」
    • この場合値オブジェクトに type Moderator = { userId, name, email } って感じで変換するのがすごい、id が含まれないのがすごい
    • たしかに下流のドメインモデルじゃないからこっちで識別子を与えるでも永続化するでもないし、id は要らなそう。で、id が要らないってことはエンティティではなく値オブジェクトになるってのがすごい
    • 13 章の統合で話が変わるか??
moriyuumoriyuu

4章 アーキテクチャ

  • 「ユーザーインターフェイスで入力のバリデーションが必要になる以上、ビジネスロジックを含めざるを得ないだろうという声もある・・・」
  • 「アプリケーションサービス (14) は、アプリケーションレイヤに位置づけられる。これはドメインサービス (7) とは異なり、ドメインロジックを持たないサービスだ。永続化のトランザクションや、セキュリティを管理するために使うこともある。また、他のシステムに通知メールを送ったり、ユーザー向けのメールのメッセージを組み立てたりするのもアプリケーションサービスの役割だ。」
    • ユースケース層ではドメインロジックを持たない、と書いてある
  • PhoneNumberStateTracker
    • 複数のイベントを受信し終わったかどうかの状態を追跡するステート(”状態追跡用オブジェクト”)。プロセス id を作っていれば、複数回同じプロセス経由でイベントを受信しても大丈夫になる(冪等)。
moriyuumoriyuu

5章 エンティティ

  • 「識別子生成のタイミング」
    • 永続化の前に早めに生成しちゃうのを推奨してそう
  • 「ドメインの識別子は、必ずしもデータベースの主キーでなくてもかまわない。」
    • そうなんだ
  • ロールがむずい。ロールのインターフェース?うちでいうとどれ
  • 「契約による設計でいうところのアサーション」って何
  • (InvalidRequestError って、Java の IllegalArgumentException から取って IllegalArgumentError とかがいいかもな。Request っていうの違和感あった)
  • エンティティのバリデーション
    1. 属性/プロパティのバ
    2. オブジェクト全体のバ
    3. オブジェクトの合成のバ
    • 自社ではまだ 1 しかやってない
    • プロパティのアクセサメソッドではそのプロパティのバリデーションしかしないつもりだから 2 とかが出てくるんだな。そのプロパティを変更しようとしたときに他のプロパティの値と整合してるかのチェックもしようとすると、コードは複雑になるしね
    • 2 ではエンティティにバリデータを定義してそれをクライアントから使うって風にしてるぽい
moriyuumoriyuu

6章 値オブジェクト

  • 自己カプセル化って何
  • 値オブジェクトのメソッドが new 自分() を返すのは別にありなんだ それは自己書換えではないらしい
  • 標準型って要は enum らしい。表現方法はいろいろあるけど結局 enum がいいって。つまり enum っぽいもののことだ。うちなら string の union type かな。
  • ORM でどうののとこは飛ばした
moriyuumoriyuu

7章 サービス

  • 「時には、単純に「物」とはできないこともある。」
  • 「使えるのは、これらのような場合」↓なるほどなあ!イメージしやすい
    • 重要なビジネスロジックを実行する
    • ドメインオブジェクトを、ひとつの構成から別の構成に変換する
    • 複数のドメインオブジェクトからの入力にもとづいて、値を算出する
  • 「実際、クライアントが持つべき唯一の業務的な責務は、単一のドメイン固有の操作を実行することだ」「アプリケーションサービス内のクライアントの責務は、タスクを調整することだけである」
    • タスクの調整ってのが、社内で言ってるいわゆる「交通整理」ですね
  • このサンプルではドメインサービスは UserDescriptor って値オブジェクトを返すらしい へえ〜
  • ドメインサービスのインターフェースの定義はドメイン層、実装はインフラ層
moriyuumoriyuu

8章 ドメインイベント

  • 「ドメインモデルの情報を、メッセージング機構のミドルウェアには漏らさないようにしよう」。メッセージング機構はインフラ層なので、ドメインの話と直結するのはよくない。(この後 11 章でエンティティのファクトリメソッドでイベント発行してたぞ???あ、この章でもエンティティのメソッドでイベント発行してるところあった。これ、エンティティの更新時に暗黙的に永続化してるオブジェクトも更新するって前提で基本書かれているからかも!だから意味分からなかったのか・・この暗黙方式だとエンティティのコードにベッタリイベント発行の記述が書かれて最悪じゃないか..?ここはドメイン層だぞ)
  • リポジトリへのアクセス時にリポジトリがイベントを発行するのが一般的っぽい。全部やってもいいし、やらなくてもいい、みたいな感じ。 エンティティだと思う。
    • 「最もありがちなドメインイベントの使用例は、集約がイベントを作成し、それを発行するという場面だろう」
  • 軽量なサンプルでは EventEmitter みたいなパターンをやってる。ただし並行処理されないようにしてる
  • サブスクライバはたった1度だけ登録していればよくて、けどどこのコンポーネントに登録させるんだ?って思ってたが、「サブスクライバ」的には、該当ユースケースのイベント発行前あたりに書いてるっぽい。まあ確かに文脈を説明するしいいかも。毎回登録解除する必要もない気がするから重複登録できないようにした上でそうするのが個人的には良さそうな気がする。
    • あ、「イベントストアへの格納と、メッセージング基盤による(別の境界づけられたコンテキストへの)転送」の場合はユースケースに書かず、「単一のサブスクライバコンポーネントを作って対応するだろう」だって。へえ。
  • 「サブスクライバがやってはいけないことがある。別の集約のインスタンスを取得し、そのコマンドを実行して状態を変更することだ。これは、単一のトランザクション内では単一の集約のインスタンスだけを変更するという経験則に反する。」と書いてある。どういうこと????今まさに XXX を集約に追加したらその追加イベントの購読時に YYY.xxxCount のインクリメントをしようとしてたんだけど、それはダメってことか?単一のトランザクションって何?これは非同期処理じゃないの? 🤔
  • 「自立型のサービスおよびシステム」
    • なんか RPC に依存しないで自分で頑張ったほうが可用性高まるしおすすめ、って言ってそう。RPC に依存しないために、ドメインイベントの発行/購読っていうのを挟んで非同期にするといいねってことかな
  • 「メッセージベースの通知の発行」
    • 「PublishedMessageTracker」追跡用のオブジェクト
    • 「このクラスはドメインモデルの一部ではなく、むしろアプリケーションに属するものであることに注意しよう」たしかに〜!
moriyuumoriyuu

10章 集約

  • 「整合性の教会の論理的な意味は、「その内部にあるあらゆるものは、どんな操作をするにかかわらず、特定の不変条件のルールに従う」ということだ」
    • 「不変条件とは、常に整合性を保っている必要のあるビジネスルールのことだ」
  • 10.2「したがって、集約は主として整合性の境界を定めるものであり、オブジェクトグラフを設計したいという理由で作るものではない」
  • 10.2「境界づけられたコンテキストを適切に設計すれば、どんな場合でも、ひとつのトランザクション内で変更する集約をひとつだけに絞り込める。さらに、トランザクションの分析をしてからでないと、集約の設計の善し悪しを正しく判断することはできない」
  • 10.3で、集約をデカくすると、リリースやスプリントにバックログアイテムをひとつだけ追加しようとしたときにも全てのバックログアイテムをメモリに読み込む必要が出てしまうと言ってる。これは集約を書き込み、読み込みする際にはその集約丸ごと全部でやらないといけないって話が前提のはず。読み込みはhiberNate で遅延読み込み対応してるって言ってるが。で、だからどうしろってここで具体的には言ってないが、集約を小さく分けろって話で、つまりこういう1:1000とかで紐づくようなやつは別集約が基本だぜって言ってる気がする。あとは値オブジェクト使うと良いって言ってるが具体的な話がないのでこの場合は無理なのかな
  • 10.3「ユースケースを鵜呑みにするな」
    • 「単一のトランザクション内での整合性の維持を求められるようなユースケースを与えられたからといって、必ずそうしなければならないというわけではない。そういった場合の多くは、複数の集約間で結果整合性が保たれていれば、それで十分に目的を達成できるものだ。」
    • 「結果整合性を維ことと、どの程度までの遅延が許されるのかを明示」
  • 結果整合性でいいかどうかは「誰が」整合性を保つ役割なのか?で考えると良いらしい。ユーザーならトランザクションで、他ユーザーやシステムなら結果。あんまわからん。エヴァンスと話したんやけど〜てイキリ
  • 10.4「モデルのナビゲーション」
    • 「識別子で参照するようにしたからといって、他のモデルの中を一切たどれなくなるというわけではない。たとえば、リポジトリ(12)を集約内から使って検索することもできる。このテクニックは切り離されたドメインモデルと呼ばれており、遅延読み込みの方式のひとつである。しかし、私がお勧めするのは、それとは別の方法だ。リポジトリあるいはドメインサービス(7)を使って、集約の振る舞いを実行する前に依存オブジェクトを検索する。」
      • エヴァンス本では説明されていたけど IDDD では推奨されてないアレだ
  • 集約のルールに違反する場合について書いてある 10.6
  • 同集約にするかどうか、やっぱり関連オブジェクトをメモリに全部乗せ前提で考えていて、大体25オブジェクトだしいけるな〜とかやってる。苦闘感あるけどけど結局そういう感じか〜
  • なんか結果整合性を使うから、クライアントにポーリングさせるか?再読み込みを促すか?とか言ってる。皺寄せはクライアントにくるな〜。「ユーザー受け入れテストで確認する必要はあるが、おそらくうまくいきそうだ」という驕り うるせえ!
  • 結局同集約にするか別集約にするかはお前で決めろって感じだった。ヒントは書いてくれたけど。DDDが銀の弾丸ではないことがこの章でわかる。つらすぎる。
moriyuumoriyuu

11章 ファクトリ

  • ドメインイベントの発行をエンティティのメソッドがやってる!ドメインの話だしそりゃそうかーという感じ
  • ファクトリメソッドとは、ドメインオブジェクトを生成するエンティティのメソッド。メソッド内にドメインロジック(xxという条件では生成できない等のガード)が入ることもある(てかそれが出来るからうれしいって感じだと思う)。これを用意する場合、ドメインオブジェクトを直接生成はできないようにしてるっぽい。
  • 他とおなじように、ユースケース層が、ファクトリメソッド経由でドメインオブジェクトを生成し、それをリポジトリで永続化、みたいにするっぽい
  • 他の境界づけられたコンテキストから受け取ったオブジェクトを自コンテキストの値オブジェクトに変換するドメインサービス(=ファクトリとしてのサービス)だって!すごい!これはファクトリだ。頭良いな〜とおもう。でもここで「コンテキストマップ」とか「DTO」とかのワードが出てないのが不安。そういうことでしょ?
moriyuumoriyuu

12章 リポジトリ

  • 「コレクション指向のリポジトリのポイント」「リポジトリから取得したオブジェクトを変更したときに、それをリポジトリに「書き戻す」必要が内容にしなければならない」
    • まじっすか。そんな何、mutable なこと許していいんですか
  • 「コレクション指向のリポジトリ」より「永続指向のリポジトリ」のほうが変更が明示的でいいですよね?!?!
  • マルチテナントをやる場合、この mongoDB の例に出てたようにコレクション名を "product" + tenantId にするのヘェ〜って感じだった。多分他のコレクションもテナント毎に分けるのかな。確かにこれなら事故りにくいのかも?よくある方法なんすかね
  • 12.3「時には、集約のルートに直接アクセスするのではなく、集約の一部分だけをリポジトリから取り出したい場合もあるだろう・・・」ありそう。気をつけろよって言ってた
  • 12.3「そんな場合は、ユースケースに最適化したクエリを使うことになる。永続化メカニズムに対して複雑なクエリを指定して、その結果を、ユースケース専用に作った値オブジェクト (6) に動的に格納する。」「場合によって、集約のインスタンスではなく値オブジェクトを返すようなリポジトリがあっても、不思議ではない。size() メソッドを提供するリポジトリは、自身が保持する集約のインスタンスの数を、シンプルな整数値で返す。ユースケースに最適化したクエリも、その延長線上にある。クライアントの要件が複雑になったので、返す値も複雑になったというだけのことだ。」
    • おお〜!ちからづよい!
    • でもこの続きで、そういうのが多く出てきたら「コードの臭い」「集約の設計ミスを覆い隠すリポジトリ」とか言われてる。悲しい。そうでしたか・・
    • でも集約の境界は間違えてないと言うのなら CQRS を使うことを考えよう、ですって。へえ〜。まあ gql 使ってるので・・
  • 12.4「ドメインモデルの永続化に関するトランザクションをとりまとめるには、アプリケーションレイヤ (14) を使うのが一般的だ」わかったけどなんで?
  • 12.6「私は、リポジトリと DAO は違うものだと考えている。」「リポジトリのことを、より汎用的な意味でのDAOと考えてもかまわない。注意してほしいのは、リポジトリを設計するときには可能な限り、データアクセス指向ではなくコレクション指向で考えるということだ。それは、データと、その永続化を管理する仕組みとしてのCRUD操作よりも、ドメインモデルにあなたを集中させるのに役立つだろう。」かっこい〜
moriyuumoriyuu

13章 境界づけられたコンテキストの統合

(読んでない)

moriyuumoriyuu

付録A 集約とイベントソーシング:A+ES

(読んでない)