#7【勉強メモ】エヴァンス本からざっくり理解するDDD

## 目標
・ふわっとした理解のDDDについて[エヴァンス本](https://www.amazon.co.jp/%E3%82%A8%E3%83%AA%E3%83%83%E3%82%AF%E3%83%BB%E3%82%A8%E3%83%B4%E3%82%A1%E3%83%B3%E3%82%B9%E3%81%AE%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88-Architects%E2%80%99Archive-%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E9%96%8B%E7%99%BA%E3%81%AE%E5%AE%9F%E8%B7%B5-%E3%82%A8%E3%83%AA%E3%83%83%E3%82%AF%E3%83%BB%E3%82%A8%E3%83%B4%E3%82%A1%E3%83%B3%E3%82%B9/dp/4798121967/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=17W0M5YAY0R9Z&dib=eyJ2IjoiMSJ9.qtIEGNumTF4Fq3OG4r7BcgPPpMccQaiAMFjoQiYsRfAUl2LJ3kucBAwXJG1pE1-9MMyozf_R6XnUk8kwhpkIPyRMzsu94DrC82a2cWHXn2TDjembTcFwG0z4uuYeu7EY_VF_p_KcGaJhu-sFQP2IfF8BXdAN9RWcUsZLGOA8hIhkuAKrptA3cagpPrF5ZoxXjjZ9ctVxNzHN-qanc6enklCzFiHeCY1UpXho238Cgd4eMsXlRTbQ5u_T9ZSbUOj5EOjKzf1jx0xSJbTwFJrRetDuJaorGcsbbNpAuJkvNAs.F3zE4YfUXPLj7DRzsSV4wM07EgTKoLqmpU491uMjIhw&dib_tag=se&keywords=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88&qid=1710421495&sprefix=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%2Caps%2C279&sr=8-1)を通して体系的に学習する
・各章の要約をアウトプットすることで知識を定着させる

## 前回の記事
第6章の内容は以下でまとめています。

https://qiita.com/kotemendo2819/items/4120a11f7a1304688c6f

## 【第7章】言語を使用する：応用例
本章では貨物輸送システムを例に、これまで第２部で紹介したパターンを組み合わせて
モデルと設計が改良されている様子を段階的に見ていく。

## 貨物輸送システムを導入する
最初の要求は3つの基本的な機能だ。
1.顧客貨物の荷役(積み下ろし)を追跡する
2.貨物を予約する
3.貨物が到着した際、自動で顧客に請求書を送付する

・荷役イベント
貨物にかにて行われる個別のアクション。
例)積み下ろし、通関、請求など

・配送仕様
配送の目的。荷出し地と到着時刻を含む。

・輸送機器移動
特定の輸送機器(トラック、船など)によって、ある位置から他の位置への移動を表す。
貨物は輸送機器に荷積されることで輸送機器移動が1回以上行われる。

・配送記録
貨物の現在位置をわりだす。
最後に行われた荷積みや荷下ろしと、それに対応する輸送機器移動の荷出し地を解析し実現される。

## ドメインを隔離する：アプリケーションの導入
アプリケーション機能を3つ識別し、それらを3つのAP層クラスに割り当てる。
1. 追跡問い合わせ
貨物に対して行われた過去と現在の荷役にアクセスできる。
2. 予約アプリ
新規貨物を登録できるよする。
3. イベント記録アプリ
貨物に対して行われた各荷役を記録する。

## エンティティと値オブジェクトを区別する
・顧客(エンティティ)
ユーザーにとって同一性を明らかにする必要がある
・貨物(エンティティ)
それぞれを区別する必要がある
・荷役イベント(エンティティ)
現実世界での出来事として置き換えることができない
・輸送機器移動(エンティティ)
現実世界での出来事として置き換えることができない
・位置(エンティティ)
内部で使用される任意の自動生成識別し等を用いる
・配送記録(エンティティ)
貨物と一対一で交換することができない
・配送使用(値オブジェクト)
同じ場所に向かっている2つの貨物がある場合、同一の配送仕様を共有することができる

### `役割とその他の属性`
そのたの履歴や連続性をもたないもの、タイムスタンプなどの属性は値オブジェクトである

## 輸送ドメインの関連を設計する
・顧客エンティティが、過去に関わるすべての貨物を持ち回ると複雑になる。
リポジトリを使用し逆方法(貨物→顧客)のアクセスを提供できる。
・貨物と輸送記録は双方向
追跡することがイベント記録アプリの中核である。記録はその対象を参照しなければならない。
・値オブジェクトは自分の所有者を参照するべきでない。
配送仕様の概念は貨物よりも配送記録と関係が深い。
・荷役イベント→輸送機器移動の方向性が決まっていることで、辿られることのないものを含んだ
多数の方向に対処しないですむ。
・エンティティは多数のオブジェクトによって使用される可能性がある。
位置がユーザーを追跡するということは非現実的だ。

## 集約の限界
・貨物は集約のルートであり、グローバルな一意の識別しをもつ
貨物がなければ存在しないであろう、配送記録等を持つ
・荷役イベントは競合の少ないトランザクションで作成する必要があるため
独自の集約のルートとなる
・そのた顧客、位置、輸送機器移動は独自の集約ルートとなる

## リポジトリを選択する
上記の５エンティティは集約のルートなのでリポジトリを持つことが検討される。
その際実際にリポジトリを持つべきものはプリケーションに立ち返って決める。

・予約アプリ
ユーザーは顧客を選択する必要がある(顧客リポジトリ)。
また荷出し地を決めるため位置も見つけなければならない(位置リポジトリ)。
・イベント記録アプリ
輸送機器移動を検索する必要がある(輸送機器移動リポジトリ)。
どの貨物が荷積みされたか伝える必要がある(貨物リポジトリ)。

## シナリオをウォークスルーする
### `サンプルアプリケーションの機能：貨物の荷出し地を変更する`
配送仕様は値オブジェクトなので、貨物のセッタメソッドを使用して新しい配送仕様で古いものを置き換える

### `サンプルアプリケーションの機能：リピータへの対応`
ユーザーとしては、同じ顧客によって繰り返される予約は似ている傾向があるため
新規貨物のプロトタイプとして古い貨物を使用したい。
→ユーザーがリポジトリから貨物を検索した後、選んだ貨物をもとに新規の貨物を生成する。
その際、貨物集約のオブジェクトや属性それぞれに何が起こるか考える必要がある。
貨物集約の境界内になるものはすべてコピーするが、境界外のものは新規オブジェクトに対し
影響を及ぼさないように注意する。

・配送記録
新しい記録を生成する。
・顧客の役割
役割をキーとした顧客を参照するマップはコピーするが、
顧客オブジェクトは集約の境界外のため顧客オブジェクト自体はコピーしない。
・追跡ID
新しい追跡IDを提供しなければならない。

## オブジェクトの生成
### `貨物用のファクトリとコンストラクタ`
プリミティブなコンストラクタを用いて生成したいのは、不変条件を満たしているオブジェクトか
エンティティの場合は同一性を担保したオブジェクトである。

```java:ファクトリメソッドを貨物上に作成する例
public Cargo copyPrototype(String newTrackingID)
```
```java:独立したファクトリにメソッドを作成する例
public Cargo newCargo(Cargo prototype, String newTrackingID)
```
```java:ID取得のプロセスをカプセル化する例
public Cargo newCargo(Cargo prototype)
```
上記のファクトリの戻り値は、配送記録が空で配送仕様がNULLの貨物である。

貨物が配送記録を含んだ集約のルートであるので両者は一緒に生成されなければならない。
なお配送記録のコンストラクタは引数に貨物をとる。
```java:貨物のコンストラクタ
public Cargo(String id){
  trackingID = id;
  deliveryHistory = new DeliveryHistory(this);
  cusotmrRoles = new HashMap();
}
```

### `荷役イベントを追加する`
```java:有効な荷役イベントを生成するコンストラクタ
public HandlingEvent(Cargo c, String eventType, Date timeStamp){
  handled = c;
  type = eventType;
  completionTime = timeStamp;
}
```
より便利でクライアントコードの表現力を豊かにするため、
単純なファクトリメソッドをイベントタイプ毎に荷役イベントに追加し、必要なすべてを
取得するようにする。

```java:荷積みイベントに輸送機器移動が含まれる場合
/**
* 新規荷積み.
* @param c 貨物
* @param loadedOnto 荷積み対象(輸送機器移動)
* @param timeStamp タイムスタンプ
* @return 荷役イベント
/*
public HandlingEvent newLoading(Cargo c, CarrierMovement loadedOnto, Date timeStamp){
  HandlingEvent result = new HandlingEvent(c, LOADING_EVENT, timeStamp);
  result.setCarrierMovement(loadedOnto);
  return result;
}
```

## リファクタリングのために立ち止まる：貨物集約についてのもう1つの設計
荷役イベントを追加する時配送記録を更新しなければならないことから
他のユーザーが同時に貨物を修正したら、荷役イベントトランザクションは失敗する可能性がある
→配送記録が持つ荷役イベントのコレクションをクエリで置き換える。荷役イベント用のリポジトリを追加する。

### `新機能を導入する`
・販売管理システム
特定のタイプの貨物をどれくらい予約するべきか配分をできるようにする機能を提供。
予約アプリと統合する。(API連携？)

### `2つのシステムを接続する`
・販売管理システムの言語との間の変換をおこなうクラスの追加
腐敗防止策として機能する
・配分チェックサービスの追加
他のシステムから取得する必要のある配分機能について、サービスを実装する

### `モデルを強化する：ビジネスのセグメント化`
他のシステムのドメインをこちら側の観点で抽象化し直すことができるように
ドメインモデルを豊かにし貨物にカテゴリが存在するという知識に適応できるようにする。

・エンタープライズエグメント
ビジネスの分割方法を定義した次元軸の集まり。
過去1ヶ月といった時間的なものに加えて、輸送ビジネスに関してこれまで述べたものをすべて含めることができる。

この設計の問題
1. 予約アプリに、ルールの適用を任せている
ビジネスルールを遂行するのはドメインの責務だからAP層で実行すべきでない。
2. エンタープライズセグメント取得方法が明確でない

### `パフォーマンスチューニング`
貨物のエンタープライズセグメントを取得するなど比較的静的なデータをキャッシュして
配分チェックサービスのあるサーバーに配置し負荷を減らすという設計の選択肢がある。

## 最後に
エンタープライズエグメントを取得する責務を貨物にもたせないのか？
・貨物が配分チェックサービスについて知ることは、貨物の責務から外れるから

## まとめ
正直2割くらいしか理解できている自信がない。。。(ご指摘お待ちしていますmm)
図がふんだんに使われていたとはいえ、架空のシステムに対して
抽象的なパターンを当てはめていたので前の章含めて何度も読み返しながら進めた。
一周した後また読み返したら見え方が変わるかもしれないという一縷の望みに賭けて次の章に進もう。

第２部は本章でおわり、次から「第3章より深い洞察へ向かうリファクタリング」に入ります。

https://qiita.com/kotemendo2819/items/77596ea89831f0074cae

参考になったらいいねやコメントおまちしています！！