Spring MVCのService層と真剣に向き合ってみた話
はじめに
プログラミングポエム記事のため答えはありません。
概要
Spring MVCはその名の通りみんな大好きMVCを採用しており、アプリケーションをいくつかの層に分けて組んでいくのですが、この層の中にはサービス層というものがあります。(エンタープライズ系のMVCによくある気がするけど名前あるの?MVC2?) サービス層は、ドメイン層におけるオブジェクトのロジックを表現するためにどうしても手続き的な処理をせねばならんところを書いていく層(マサカリポイント)なのですが、この辺に何を書くかは結構ざっくりしちゃってる印象があります。
なんでこの辺どうすればいいかちょっくら考えてみました。
現状のSpring MVCについて思うこと
ぶっちゃけTERASOLUNA Frameworkに沿ってやってけばいいんじゃないかなーと思ってます。 といってもそのまま使うんじゃなくて、基本的にはSpring BootのプロジェクトをSpring Initializrで適当に作って、あとは必要そうなところだけ沿っていけば百点満点とは言わずとも悪くはない構成になるとは思います。他にもデータベースアクセス層をJQQQとかDoma2に変えるとか、バッチをSpring Batchでやるとかちょこちょこやりやすいように変更を加えていく感じ。
TERASOLUNA FrameworkにおけるService層
Service層で迷う点
Spring MVCにおいて@Transactionalによるアノテーションベースのトランザクションをかけようとした場合、Controllerから呼び出すServiceクラスのメソッドは一つにしなければならないのですが、悲しいことに処理は往々にして単一でシンプルなものにならず、複数のServiceを呼び出したくなる時があります。
この時、Controllerから直接呼び出すServiceクラスをトランザクションのコンテナとして扱い、その中で他のServiceを呼び出してやるという手法を取ることが多いのですが、これ本当にいいのか?と最近思うことがあります。
例えば、アグリゲーションの中にアグリゲーション入れようみたいなネストしたオブジェクトをデータベースから持ってくるパターン。いや設計クソだから変えろよみたいなのはまあそれはそうなんですが、やらざるをえん時もあるんですよ。辛いことに。
こういう場合、ServiceがServiceを呼び、さらにそのServiceがServiceを呼びと、TERASOLUNA FrameworkにおけるSharedServiceパターンだとどれがSharedServiceでどれがApplication Serviceだよみたいな感じになりかねんなー、と思ってます。
じゃあどうすんの?
Service層にAtomic Designの考え方適用できないかなー、とぼんやり思ってます。
Serviceに粒度のレベルを設け、パッケージ単位で厳密に区切っちゃうことでうまいことService層をまとめ、下位のServiceは上位のServiceからしか呼べない、という、より詳細化したSharedServiceをパターンとしてまとめられないかということを考えてるのですが、じゃあそれどういう単位で切るのとか、アプリケーションの要綱によって必要かどうかわからんとか、そもそも最小粒度のServiceってModelに突っ込むべきなんじゃねーかとか、ドメインモデルの設定が狂ってるからそんなことになるんだよとか、パターンにするにはわりと限定的な状況にしか適用できない以上、うまいこと設計パターンに落とし込むにはいろいろと引っかかりを覚える部分があります。
パターンとは言わないまでも一つの考え方として以下の感じでどうだろう、と考えています。
- Serviceの基本単位はShared Serviceとする
- Transaction ContainerとしてServiceのコンテナを置く。
- Shared Serviceをいくつかまとめて一つの処理とした集約サービスクラスを置く。
- 基本的にControllerからはTransaction Containerしか呼ばない
- Transaction Containerには集約サービスとShared Serviceを組み合わせてエンドポイントとか処理単位ごとに呼び出したい処理の流れを構成する
とかどうかなあ。冗長すぎるよなあ。
まとめ
モデリングをしっかりしないとこんなハチャメチャに複雑な構成を作らざるを得ない状況になるので、設計者は肝に銘じようね。