要点
- 共通化の判断基準は「フィールドが同じかどうか」ではなく「同じドメイン概念かどうか」である。
- 異なるドメインのレスポンスを 1 つの型階層に束ねると、片方の仕様変更が他方に波及する。
- 見た目の一致は偶然の一致(Accidental Duplication)であり、DRY 原則の適用対象ではない。
問題: 似ているから共通化したコード
|
|
問題は sealed class という構文ではない。異なるドメイン(クレジットカード決済と銀行振込)を 1 つの型階層に束ねている点 が問題である。
サーバー API がエンドポイントを分けて設計しているということは、クレジットカードと銀行振込は別のドメイン領域である。サーバー側が分離している境界を、クライアント側で統合してしまっている。
解決: ドメインごとに分離する
共通化した型階層を解体し、API クライアントごとに Request / Response を閉じて定義する。
|
|
こうすることで、ドメイン境界が明確になり、不要な共通化が入り込む余地がなくなる 1。
なぜ共通化すべきでないのか
DRY 原則の誤用
DRY 原則の本質は「同じ知識を二重に持たないこと」である。フィールド構造の一致は、知識の一致を意味しない。
クレジットカード決済のレスポンスと銀行振込のレスポンスは、別の API エンドポイントのレスポンスであり、別の責務を持つ。たまたまフィールドが同じでも、それは同じドメイン領域の知識ではない。これは 偶然の一致(Accidental Duplication) である。
YAGNI 原則
「将来コンビニ決済が追加されるかもしれないから、共通の親クラスを作っておこう」— これが YAGNI 原則への違反となる。
- コンビニ決済が追加されるかどうかは、その時点ではわからない。
- 追加されたとしても、レスポンスのフィールドが同じとは限らない。
- 追加されたとしても、継承で解決すべき問題とは限らない。
先回りした抽象化が、かえって設計を複雑にし、メンテナンスコストを上げることになる。
SOLID 原則の観点
SRP・ISP の観点からも、異なるドメインを 1 つの型階層に束ねることは問題になる。これらの原則については以下の記事で扱っている。
加えて、開放閉鎖の原則(OCP) の観点でも問題がある。コンビニ決済を追加する際、when (response) の網羅チェックにより既存の利用箇所にも修正が波及する。プロバイダごとにレスポンス型を独立させていれば、追加は新しい型と処理を足すだけで済む。
何を共通化すべきで、何をすべきでないか
判断基準は「同じドメイン概念か否か」であり、フィールドの一致ではない。
| ケース | 判断 | 理由 |
|---|---|---|
data class Book が複数の API レスポンスに含まれる |
共通化 OK | 「書籍」はドメイン概念そのもの |
| クレジットカードと銀行振込のレスポンスが同じフィールドを持つ | 共通化 NG | 異なるドメインの偶然の一致 |
FeaturedBook のケース
data class FeaturedBook は Book と同じプロパティに加え、特集掲載用のプロパティを持つ。「FeaturedBook は書籍である」というドメイン上の関係は実在する。
しかし、API レスポンスは API の構造を写すものであり、ドメインモデルではない。 異なる API のレスポンスを継承関係にすると、片方の API 仕様変更が他方に波及するリスクがある。
ドメイン上の関係は、ドメインモデルで合成により表現すれば良い。
|
|
まとめ
- 共通化の判断基準は「フィールドの一致」ではなく「ドメイン概念の同一性」である。
- API レスポンスは API の構造を写すものであり、ドメインモデルではない。
- ドメイン境界を越えた共通化は、将来的な変更コストを増大させる。
-
Request / Response を API client interface 内にネストする方法は一例である。API ごとにパッケージを分けて独立した class として定義するアプローチも有効である。重要なのは「クレジットカードと銀行振込のレスポンスが独立していること」であり、具体的な配置は設計方針に従えばよい。 ↩︎