全体の感想
ソフトウェア設計と切り離せない結合について、結合にはどんな種類があり、どう向き合うべきかを体系的に学べた本でした。
単に結合は弱ければ弱いほどよい、モジュールは細かく分ければよい、という話ではなく、複雑性・モジュール性・変動性・結合の距離などをあわせて設計判断するという考え方が特に印象に残りました。
結合を考えることは、変更の影響範囲やモジュール境界、データモデル、トランザクション境界、アーキテクチャを考えることそのものだと感じました。具体例が多く、抽象的な概念も実装に結びつけて理解しやすかったです。今年読んだ技術書の中でも特に良かった一冊でした。
読みながら理解したこと
統合コントラクト
一番しっくりきた理解は、統合コントラクト = 相手とどうやり取りするかの約束事です。
言語レベルの interface だけを指すわけではなく、REST API、GraphQL Schema、gRPC、SMTPなども統合コントラクトになりえます。
重要なのは、内部実装ではなく公開された約束だけを相手に見せることです。内部のDBや処理手順を変更しても、コントラクトが変わらなければ利用側への影響を抑えられます。
モデル結合
モデル結合は、違う文脈で同じモデルを再利用してしまい、相手の実装詳細まで共有してしまう結合と理解しました。
たとえば Management と Analysis が同じ SupportCase を扱っていても、目的が違えば必要な情報も違います。そこで Management の内部モデルを Analysis がそのまま使うと、Management 側の都合によるモデル変更に Analysis まで巻き込まれます。
DDDの言葉でいえば、異なる Bounded Context で同じモデルを共有してしまうイメージに近いです。
モデル結合とコントラクト結合の違い
- モデル結合: 内部モデル、つまり実装の詳細まで共有します
- コントラクト結合: 公開すると決めたAPIやDTOなどの約束だけを共有します
モデル結合では、相手の内部モデルの変更理由まで共有しやすくなります。コントラクトを境界に置けば、内部の変更をカプセル化できます。
カプセル化
カプセル化は、内部実装を隠して、必要な操作だけを公開することです。
この本のモジュール設計の文脈では、内部モデルやRepository、DBなどを外から直接触らせず、公開APIやコントラクトだけを見せることと捉えると分かりやすかったです。
モデル結合は本来隠したい内部モデルまで見せてしまうため、カプセル化を崩しやすいです。一方、コントラクト結合は公開境界を明示できます。
統合強度と変更コスト
統合強度が高いほど、相手の変更に巻き込まれやすくなり、変更の影響範囲が広がることで変更コストも高くなりやすいです。
自分の中では、次のように覚えると分かりやすかったです。
統合強度が高い = 相手の変更に巻き込まれやすい = 変更コストが高くなりやすい
ただし、この本では統合強度だけを単独で評価しません。
均衡結合モデル
均衡結合モデルは、これまで個別に出てきた以下の指標をまとめて、結合全体のバランス、つまり変更しやすい設計かどうかを考えるためのモデルと理解しました。
- 統合強度: どのくらい強く依存しているか
- 距離: 結合しているもの同士がどれくらい離れているか
- 変動性: どれくらい頻繁に変更されるか
重要なのは、強い結合が必ず悪いわけではないことです。
強く結合する必要があるものが近くに置かれ、一緒に変更しやすいなら、その結合は合理的な場合があります。逆に、強く結合し、頻繁に変わり、しかも遠く離れていると変更コストが大きくなります。
つまり、結合を弱くすること自体が目的ではなく、強度・距離・変動性のバランスを取ることが目的です。
アトミックな変更とトランザクション境界
SupportCase と Message の例が印象に残りました。
最後のメッセージからSLA以上の時間が経過しているかを確認し、その後 SupportCase をエスカレーションする処理を別々に行うと、確認と更新の間にエージェントが返信する可能性があります。
1. 最後のMessageを確認
2. SLA超過と判断
3. その間にエージェントが返信
4. SupportCaseをEscalatedに更新 結果として、返信済みなのにエスカレーションされるという、本来存在してはいけない状態が生まれます。
ここから、本来ひとまとまりで成功・失敗すべき変更にはトランザクション境界が必要だと理解しました。
設計上、何でも別々に変更できる柔軟性を与えることが常に良いわけではありません。本来一緒に変更すべきものまで別々に変更できると、中間状態、リトライ、補償処理、冪等性など、余計な複雑性を生みます。
Aggregate は、そのような一貫性を守るためのトランザクション境界として理解すると分かりやすかったです。
Ports & Adapters とレイヤー
いわゆる同心円の図の考え方です。
従来のレイヤードアーキテクチャではプレゼンテーション層とデータアクセス層を上下に分けますが、Ports & Adapters では、HTTP Controller も PostgreSQL Repository も ドメインから見れば外界との接続を担当するアダプターとして外側に置かれます。
そのため、プレゼンテーションの役割がDBと同じになったわけではなく、どちらもビジネスロジックではない技術的詳細として Infrastructure 側に整理されます。
依存方向は外から内へ向けます。
Infrastructure
↓
Application
↓
Domain Domain は HTTP や PostgreSQL といった技術詳細を知りません。
Domain と Application の役割
ドメインロジック
ドメインロジックは、そのビジネスだからこそ存在するルールや判断です。
例:
- SLA時間内に返信がなければケースをエスカレーションします
- クローズ済みケースには返信できません
- 特定の状態ではエスカレーションできません
アプリケーションロジック
Application Layer はドメインロジックそのものではなく、ドメインロジックを使ってユースケースをどう進めるかを調整する司令塔です。
例:
- RepositoryからSupportCaseを取得します
- Domainに処理を依頼します
- Repositoryへ保存します
- 必要なら通知や外部サービスを呼びます
- トランザクションや一連の処理を調整します
自分の中では以下の整理が分かりやすかったです。
- Domain = ビジネスルールです
- Application = ビジネスルールを使ってユースケースを進行する司令塔です
- Infrastructure = HTTP、DB、メールなどの技術的な実装です
この本を読んで残った考え方
ソフトウェアをモジュール化するとき、単に依存を減らす、サービスを分割する、抽象化する、という判断では足りません。
大事なのは、何が一緒に変わるのか、どれくらい離れているのか、どれくらい頻繁に変わるのか、どんな情報や実装を共有しているのかを考えることです。
強い結合には強い結合なりの理由があり、無理に切り離すと別の複雑性が生まれることもあります。逆に、独立して変えたいものが内部モデルまで共有しているなら、その境界は見直したほうがよいです。
結合を良い・悪いの二択で見るのではなく、変更コストとのバランスで設計判断するという視点が一番の学びでした。
ソフトウェア設計と切り離せない結合について、結合にはどんな種類があり、どう向き合うべきかを学べました。複雑性やモジュール性、変動性、距離など、いろいろな観点から設計を考えられて、とても勉強になりました。結合を考えることは設計を考えることそのものだと感じました。今年読んだ技術書の中でも特に良かった一冊です。