著者が勉強している状態
相性がいいと思った理由
REST APIとDDDの相性がいいと思ったのは、エヴァンス本の第2部第6章 ドメインオブジェクトのライフライクル の集約の節に記載されているこの文章を読んで思った。
エンティティと値オブジェクトを集約の中にまとめ、各集約の集約に境界を定義すること。 各集約に対してルートとなるエンティティを1つ選び、 境界の内部に存在するオブジェクトへのアクセスはそのルートを経由して制御すること。
上記言葉の例として、エヴァンス本では車の例が上がっていたので、そのままの例を使用してみます。
集約ルート
- 車
エンティティ
- 車
- ブレーキ
- タイヤ
- エンジン
ブレーキの状態を確認するときに、ブレーキを直接参照しない。
車という集約を通して、ブレーキの状態を参照する。
以下は。車の集約ルートを通して、ブレーキが交換時期なのかを確認する。
public class Car { private String id; // ID private Brake brake; // ブレーキ private List<Tire> tires; // タイヤ private Engine engine; // エンジン // ブレーキ交換時期 private boolean brakeTimeToReplace(){ return this.brake.timeToReplace(); } } public class Brake { private String id; // id private int mileage; // 走行距離 // 交換時期 private boolean timeToReplace(){ // 7万キロ以上走ったら交換時期 return this.mileage >= 70000; } }
集約をエンドポイントで表現しようとしたときに
このブレーキの例がそのまま、URI で表現できるのではないのかと思った時に、自分の中で電撃が走りました。
REST APIでは、1つのリソースに対してアドレス可能なURIを用意するので、エンドポイントに悩んだときに、適切な構造を探し続ける思想の DDD とマッチすると思ったからです。
// 集約を意識したエンドポイント http://localhost:8080/car http://localhost:8080/car/{carId}?type=brake // 集約を意識しないエンドポイント http://localhost:8080/car http://localhost:8080/brake?brakeId={brakeId}
カーディーラー や 車検 のユースケースでは、ブレーキのエンドポイントを露出させる必要はありませんが、修理 のユースケースでは、ブレーキそのものが集約となって、ブレーキのエンドポイントを露出させる必要があります。
そう考えた時に、外部に対して何を提供したいのか、言い換えると適切なエンドポイントは、集約の単位で行うべきではないかと考えています。
ただ、エンティティのエンドポイントは集約で良いと思いますが、値オブジェクトは集約を意識せずに提供してもいいと思います。
とはいえ現実的に
あくまで、以下の2点から現実的には維持するのは厳しいと思います。
- エンドポイントが変わることによる外部影響
- 性能上の問題
エンドポイントが変わることによる外部影響
DDDではリファクタリングを続けることによって、システムにとって適切な構造になるように努力します。 そうなると、エンドポイントで提供していた構造と、リファクタリングによって変更された構造と大きく変わってきてしまいます。
どこかでバージョンアップして、適切な構造を提供するようになると思いますが、同期は取れないですし、そもそも構造の代わるバージョンアップに対応するのって結構めんどくさいですからね。
あまりにも大きい変更を繰り返すと嫌がられるとは思うので、古いバージョンも提供しつつ、新しいバージョンも対応するようになると思います。
全く関係ありませんが。 VISAとか、変更箇所を通知して1年後にようやく変更するらしいですね。
性能上の問題
適切な集約の構造で提供した結果、エンドポイントがバラバラになって1画面表示するのに何度もアクセスが走ってしまう可能性もあります。
そうなったときは、REST APIの原理とは離れますが、なんでも取得する頭のいいAPI(皮肉)として提供するか、graphQL等で提供するしかないんじゃないでしょうか。(思考放棄)
REST API も DDD も性能問題に関しては、解決方法を持っていないので、対応できないと思ってます。
まとめ
- リソースをURLで表現するREST API と 構造を探求し続けるDDD は相性はいい(はず)
- ただし、現実問題として相性も合わない点もある
- エンドポイントは、エンティティなら集約ルート、値オブジェクトはそのまま提供した方が良い(と思う)