はじめに
こちらへ記載してあることは一個人の考え方・理解であり、間違っている箇所もあることを前提に見ていただけると幸いです。
アウトプットのつもりで、自分なりにまとめていこうと思います。
5章 ソフトウェアで表現されたモデル
モデル(オブジェクト)の関連は実装すると難しいので...
モデルを表現する3パターンの要素に区別することに専念する!
- エンティティ
- 連続性・同一性をもつもの
- 値オブジェクト
- 状態をもつ属性、同一性をもたない
- サービス
- 操作をするためのもの(ドメイン層・インフラ層)
モデル(オブジェクト)同士の関連に気を付ける
現実では多対多や双方向の関係になるものが多いがそれを開発するのは困難...
モデル同士の関係をシンプルにして制限することが大事。
↓ そのためには
- 関連の方向を一方向にする
- 国---大統領---人 → 国---大統領--->人
- どちらが重要かを考えて一方向に
- 国---大統領---人 → 国---大統領--->人
- 限定して多重度を減らす
- 国---大統領--->人 → 国[期間]---大統領--->人
- 期間をつけることで1人に限定できる
- 国---大統領--->人 → 国[期間]---大統領--->人
- 本質的じゃない関連を除去する
- 国[期間]---大統領--->人--->(銀行口座)⇦ここを削除!
- 情報があってもモデルの本質と関係がなければ消す
- 国[期間]---大統領--->人--->(銀行口座)⇦ここを削除!
エンティティ(連続性・同一性をもつもの)
例えば、人は、生まれて->成長して->老いて->死ぬわけで...
属性(名前・年齢・住所)が変わっても、その人自体は同一で連続している。
よって、エンティティとは
時間が経ち、違う形で表現されていても同一であり、他のものとしっかり区別できるものである。(「顧客」であったり新幹線の指定席の「座席番号」もエンティティ)
エンティティが同一で、他のものと違うことを「保証する操作」が必要。→各オブジェクトに対して一意になる値(ID)を使う方法がある。
- IDはシステムによって自動生成されることが多い
- 可変でも良い
- システムの境界を超えるときは、どのようにIDを共有・連携するかを気を付ける(例外処理も忘れず)
- 配達サービスの追跡番号のようにユーザーが目にするケースもある
値オブジェクト(状態をもつ属性、同一性をもたない)
「何」であるかが問題であって、「誰」や「どれ」かは重要でないこと。
例えば...
本物の絵画と偽物の絵画はどれだけ同じように描かれていても価値が違ってくる。→エンティティ
一方で...
青色のペンと別の青色のペンがあったとき、青色が書ければ2つが同一であるかどうかは重要ではない。→値オブジェクト
- 属性(「何」であるかが大切)
- ただしペンの識別がビジネスに影響する場合は、エンティティになる
- エンティティなのか値オブジェクトなのかをしっかり区別すること
値オブジェクトの使い方
- 他の値オブジェクトで構成されることがある
- (住所:[県] [市] [番地])
-
エンティティを参照することができる
- [経路]--->[名古屋]:出発
- [経路]--->[東山線]:路線
- [経路]--->[栄]:到着
-
エンティティの属性として使用できる
- タッキー([年齢][性別][住所])
値オブジェクトの設計
値オブジェクトはコピーできて・共有できるけどイミュータブルでなければならない。
例えば...
A:田中太郎 --- コピー ---> B:田中太郎
AもBも同じ田中太郎という名前を共有できる。
でもAさんの都合で田中太郎から「伊藤太郎」になるとBが共有できない!
なのでイミュータブルにしましょうということ。
値を変えたい時は新しく「伊藤太郎」を作って置き換える。
基本的にはコピーすることを選ぶ、ただし数が多くなりがちである。
共有を選ぶ時は↓
- データベース内でオブジェクトの数の節約がとても重要
- 共有オブジェクトが完全にイミュータブル
- 通信のオーバーヘッドが低い(中央集中型サーバなど)
設計する上でのポイント
- 値オブジェクト同士は双方向の関連は起こらない
- 住所→県ではあっても、住所⇄県でにはならない
- 双方向の関連になるときは、値オブジェクトではなくエンティティでは?と考える
サービス(活動や行動)
サービスは実態よりも活動、つまり名詞じゃなく動詞ベースで命名される。
※ただし操作名は「ユビキタス言語」と合わせること。
サービスは引数も結果もドメインオブジェクトになるべきである。
すべてのふるまいをサービスにいれるのはダメ。
※ただしエンティティや値オブジェクトにもふるまいを持たせるのは良い。
サービスにしたほうが良いのはこんなとき!
- 操作がドメインの概念に関係していて、その概念がエンティティや値オブジェクトに入れるのが不自然
- 独立したインターフェースが持てる
- 操作に状態がない。
- サービスがどういった順で使われたかは結果に影響しない
サービスはアプリ層・ドメイン層・インフラ層に存在する。 ちゃんとどの層にあるのが適切であるかを検討すること!
クライアント
↓入力
アプリケーションサービス
- 入力値を理解
- ドメインサービスにメッセージを渡す(ドメイン層へ)
- インフラサービスを使って通知する(インフラ層へ)
- 結果をエクスポートする(クライアントへreturn)
ドメインサービス(ビジネスに影響)
- 口座オブジェクトや元帳オブジェクトとやりとりして振り込みや引き落としをする
- 振替結果を記録する(インフラ層へ)
- 振替結果を返す(アプリサービスへreturn)
インフラサービス
- 電子メールを送信する
- DBに記録する
モジュール(別名:パッケージ 意味ある部分をまとめる)
- 高凝集:概念の集まった意味がある集合をまとめる。
- モジュールの中にエンティティや値オブジェクトやサービスが含まれている状態
- 全体のことを考えずにモジュール内部に集中できる
- モジュールの中にエンティティや値オブジェクトやサービスが含まれている状態
- 低結合:それぞれの概念は独立して理解でき、変更できる。
- モジュールの内部がわからなくても、モジュール間の関係性だけを考えられる
モジュールにもユビキタス言語と合わせた名前をつける。(例:顧客など)
モジュールもモデルやコードと一緒にリファクタリングしていく。
モジュールを変更するときはコード変更の影響が大きいが、そこは踏ん張りどこでリファクタリングをしていく。
レイヤー化アーキテクチャを前提に分けるのが好ましい?(アプリケーション層にあるモジュール、ドメイン層にあるモジュール、インフラ層にあるモジュール...みたいに)
モデル駆動設計を実践するには
モデル駆動設計を実践し、システムに落とし込むにはどうするか?がテーマ
「オブジェクト指向設計」が主流になっている。
それはなぜか...
- オブジェクトモデリングの概念はシンプルだが、ドメインについての重要な知識が得られるくらい表現豊か
- オブジェクト指向を実現するための技術がたくさんある
- 技術に対する開発コミュニティや設計文化が成熟しており、ハマった時に解決しやすい
オブジェクト指向設計以外にも表現したいものに合わせてツールを組み合わせていくこともなお良い。(ルールエンジン・ワークフローエンジンなど)
※ただしオブジェクト指向と非オブジェクトを組み合わせるときの注意
- ドメインやモデルの概念を表現できるものを選ぶ
- ユビキタス言語でモデルを統一する
- UML図にこだわらず、他の表現も使っていいことを考える
- ツールを使うのが正しいか、複雑になりすぎていないかを常に考える