【Javaでドメイン駆動設計を実現する-3】Application層を変更に強くする
SpringBoot的には@Service。
これのつづき。
サービスクラス、ごちゃごちゃしがち問題
UI(Presentation/Controller)層からの要求に応じてドメインオブジェクトを返すのがApplication層(サービスクラス)の仕事。
それだけのはずなんだけど、サービスクラスは常にごちゃごちゃしがち。その原因を整理して、スッキリスリムなサービスクラスを作れるようになりたい、ものである。
原因1・ドメインオブジェクトに必要なメソッドが足りていない
ちょっと業務ルールが変わる。その変更をドメインオブジェクトで吸収しきれずに、サービスクラスにif文を追加して対応しちゃう。
その場では楽だけど、その「とりあえずif文追加」が積み重なって、気付けばサービスクラスがif文だらけ。
あるある。
ということで、サービスクラスにif文を書きたくなったら、その場ではちょっと大変でも、ドメインオブジェクトの設計を見直して、そちらにロジックを閉じ込めるようにしよう。
原因2・Presentation層の複雑さに振り回されている
プレゼンテーション層=ユーザーが使う画面のこと。
いくら、オブジェクト指向らしい設計にしよう、クラスは関心事別に細かく細分化して……とはいっても、画面までそれをやっちゃうと、
「この画面は在庫数を検索する画面です。商品コードを入れると現在の在庫数が出ます」
「この画面は在庫を修正する画面です。商品コードと変更したい在庫数を入れて下さい。修正画面なので現在の在庫数は見れません」
「在庫を変更したらまた検索画面から商品コードを入れて下さい」
じゃあ、いくら何でも使いにくすぎる。
なので、画面側は
「この画面は、在庫数を表示して、訂正できて、訂正したらすぐ最新の情報が表示されます」
という様に作りたい。
すると、このUI層に対応するサービス層は、表示・更新・また表示、という処理が混在することになる。
結果、ごちゃつく。
そこで、ここでも「小さく分ける」を実行して、「最新の在庫数を表示する」と、「データを更新する」の二つのサービスに分けて用意する。
さらに、Application層内にその二つのサービスを組み合わせて「表示」→「更新」→「表示」を順番に実行させるようにすることで、一つ一つのサービスを簡潔にして、小さくして、変更を容易にする。
原因3・データベースの都合に引きずられてしまう
データベースへのデータの入出力手続きの都合がサービス層にはみ出してきてしまうと、これまたごちゃごちゃ化の原因。
データベースへの入出力手続きと、業務上の「記録」「読み出し」「変更」「削除」の手続きは似ているので混同して書いてしまいがちだけど、ここをプログラマの脳内変換に頼って混同して書いていると、どう脳内変換したのか解らなくなって、結果変更しづらいコードができあがってしまう。
そこで、「業務上の関心事」と「データベースの操作」をキッチリ分離して、データベースの操作に関する記述がサービス層に漏れてこないように封じ込めたい。
具体的にどうするかというと、ここでインターフェイス機能を活用。
まず、ドメインモデルの中にインターフェイスとして「リポジトリ」を宣言しておき、そこに必要な機能を定義。
例えば
interface StockRepository{
boolean canShipping(Amount amount);
Amount Stock();
void Shipping(Amount amount);
}
ってな感じで、「出荷可能な在庫があるか調べる」「在庫数を表示する」「出荷する(在庫を減らす)」という、業務上の関心事だけを記述したリポジトリをドメインモデル内に用意。
そして、DataSource層に
class StockDataSource implements StockRepository{
// インターフェイスで定義した各メソッドの実装
}
を用意して、実際のデータベースとのやりとりはここに押し込みます。
そして、サービス層はインターフェイス型を使ってデータをやりとりするようにすることで、サービス層にデータベースの都合が漏れ出てくることを防ぐ。
以上の三つの複雑さの原因に気を遣うことで、サービス層を簡潔に保つことができ、結果としてアプリケーション全体が変更に強くなります。
続きはこちら
この記事が気に入ったらサポートをしてみませんか?