松岡幸一郎さん著 ドメイン駆動設計 サンプルコード&FAQ を読んだ感想です。書籍の要約ではなく、自分が参考になった箇所や疑問に思った箇所を書きます。
ドメイン駆動設計 モデリング/実装ガイド はモデリングや実装パターンの総論的な書籍でしたが、サンプルコードFAQはもう少し具体的なサンプルコードやFAQがメインでした。
読む順番としてはモデリング/実装ガイド → サンプルコードFAQが良いと思います。
エンティティとORマッパーの違い
- エンティティ: ドメインモデルをそのまま表現したクラス。
RDBのテーブルに関する知識やORマッパー固有の実装に影響を受けないようにする
- ORマッパー: データベースにプログラムのデータをマッピングするクラス
ORマッパーのクラスは、インフラ層のリポジトリの実装クラスの中だけで使用し、ドメイン層とユースケース層では参照されないようにします。前節で解説した通り、ORマッパーのクラスをそのままエンティティやドメインオブジェクトとして使用することは非推奨です。
(P.36)
かなり腑に落ちた部分
RailsやLaravelで保守性が低下しやすいのはModelクラスをエンティティとして利用しているのが大きそうです。
ORマッパーをエンティティとして使用するのはドメインモデルを正確に反映できない(DBのテーブル設計に制約されるため)・インフラ層の知識がドメイン層に漏れ出すという2つの理由でアンチパターンだと理解しました。
リポジトリでソートするにはどうすれば良いか
- リポジトリ層のメソッドの引数に「並び替え条件」を表すenumを渡す
ソート条件をenumで定義すると不正な値がソートキーに渡ることを防止できます。
ドメイン層にソートに関する知識を定義して良いのか、と思いましたが「ユーザエンティティの名前」や「ユーザID」のようにドメイン層の知識で表現されていれば問題ないようです。
(逆にorder by やlimitのようにソート・絞り込みを実現する具体的な技術の情報がドメイン層に流出しているのはNGです)
ただ、昇順降順指定はどのように実現するか書かれていませんでした。
ソートが昇順か降順かという情報をドメイン層に持たせるのは不自然なので、リポジトリで制御するんでしょうか...
ユースケースオブジェクトのテストでエンティティのアサーションは必要か
- ユースケース層の責務はおおまかに言って「エンティティクラスのインスタンスを生成してリポジトリに渡す」ことなので、改めてインスタンスのプロパティをアサーションする必要はない。(たただし、ユースケース層とドメイン層の結合テストという観点では実施しても良い)
ユースケース層でエンティティのテストを省略するためにもエンティティクラスのテストはちゃんとやっておく必要がありますね。
(クラスの責務を明確にするとテストの重複を減らしやすく、品質を担保しやすいと思いました)
各レイヤーで送出したレイヤーはどのように処理するか
プレゼンテーション層に共通の例外ハンドラーを定義し、そこで例外をキャッチしてエラーレスポンス用のオブジェクトに詰め替えるのがよいでしょう。
(P. 106)
- 例外の送出: 各レイヤーの責務
- エラー情報の表示: プレゼンテーション層の責務
と整理できるでしょう。
例外ハンドラーはフレームワークの実装に依存します。
フレームワークの実装に則る方が実装コストが低いというのもあると思いますが、クライアントとのデータのやり取りはプレゼンテーション層の責務であるというのが一番の理由だと思います。
業務ロジックとは?
この書籍では、「業務ロジック」という言葉が示す範囲が広すぎるのでより詳細化した言葉で分類すべきとされています。
- ドメイン知識: ソフトウェアで問題解決する領域に存在するルール/制約。 ソフトウェアが無くても存在する。 ドメイン層に記述
- ユースケース: ユーザがソフトウェアで行う操作。 ソフトウェアありきのもの。 ユースケース層に記述
- 表示に関するもの: 書式やフォーマットなど、表示時のルール。 プレゼンテーション層に記述
私は表示ルールをドメイン層の関心事としてとらえてしまいがちなので、この部分は特に参考になりました。 表示形式を整える処理はプレゼンテーション層のコントローラとは別のパッケージで管理すると責務が明確になりそうです。
まとめ
リポジトリのソートや例外処理など疑問に思っていたことが解消されました。
本書ではテストが丸々1章を割いて扱われていましたが、十分に理解することができませんでした。