Spring Bootアーキテクチャ ベストプラクティス
Spring Bootアーキテクチャ ベストプラクティスのついて記述する。
柔軟に機能の追加や修正をしたい
開発チームで柔軟(機能追加・修正・運用がしやすい)なソフトウェアを開発するためにはどのような構造が良いか?
チームメンバーのスキルセットにも依存せずに、なるべく開発しやすいようなアーキテクチャを思考する。
前提
- アーキテクチャに正解はない。
- ドメイン駆動設計を意識する。
パッケージアーキテクチャ
わかりやすくするために構成を図で示す。
1 | Architecture |
controllerパッケージ
- v1 - バージョン1のAPIを実装
- v2 - バージョン2のAPIを実装
@RestControllerを実装する。
APIエンドポイントやHttpMethodを定義し、バージョンは適宜増やせるような構成にしておく。
domainパッケージ
- entity - Entityクラス
- データベースに関連するテーブル構造を扱う。
- model - Modelクラス
- メモリに保持したいデータ構造を扱う。
- repository - データベースを扱うCURDクラス
- データベースにアクセスして、結果を返却する。
- service - ロジックを持つServiceクラス。
- repositoryやEntity、modelを扱ってロジックを持つ。
- entity - Entityクラス
common
- config - Beanクラス
- 独自@Beanクラスを保持する
- property - Propertyクラス
- Springの@Configurationをクラスを保持する。
- util - Utilクラス
- staticメソッドを保持し、共通して利用する計算処理を行う。
- enum - Enum
- entityにmodelに依存しない場合のenum。依存している場合は、インナークラスで定義し管理しやすくする。
- config - Beanクラス
exception
- exception - エラーハンドリングクラス
- @ControllerAdviceで利用する独自Exceptionクラス。
- 独自なライブラリのExceptionクラス。
- exception - エラーハンドリングクラス
security
- role - Roleクラス
- Spring Security等でauthorityをコントロールする。
- filter - Filterクラス
- Spring PreAuthenticatedProcessingFilterの処理を行う。
- role - Roleクラス
task
- task - Spring Sheduleクラス
- Cronジョブの定期処理をする。
- task - Spring Sheduleクラス
controllerについて
controllerパッケージは、domain内の@Component、@ServiceのBeanをInjectする。
controllerパッケージは@Controller、@RestControllerのクラスを格納する。
throwされてきた例外クラスを想定した構成にしておく。
Unitテストも同様にして、実装。
domainについて
domainパッケージは、domain内の@RepositoryクラスをInjectする。
propertyクラスもケースbyケースでInjectする可能性は十分にある。
また、例外のcatch処理をなるべく厳密に行う。
Unitテストも同様にして、実装。
commonについて
BeanクラスやPropertyやEnumを保持する。
BeanクラスやPropertyでない場合は、staticなアクセス制御にする。
Unitテストも同様にして、実装。
exceptionについて
例外クラス。
APIでSpring Bootを利用する場合は、APIエラーレスポンスクラスを定義する。APIであってもエラーレスポンス以外の例外クラスを定義したい場合にも、このパッケージで細かく実装していく。
Unitテストも同様にして、実装。
taskついて
Schedule処理するクラス。
Unitテストも同様にして、実装。
DIコンテナのアーキテクチャ
構成図。
Spring Bootでソフトウェア開発するときのDIコンテナのアーキテクチャを思考した。
パッケージ構成でもある程度DIコンテナの依存も想定できるかもしれないが、DIコンテナの思想も整理しておきたい。
@Controllerや@RestControllerは@ServiceクラスをInjectする。
@Serviceは@Componentや@RepositoryをInjectする。
また、独自実装したFactoryクラスやSingletonクラスがある場合は、@Component内でインスタンス生成を行いたい。
@Service側はあくまで@Componentを利用するだけにすることで、Unitテストも書きやすくなると考えられる。