Spring Boot プロジェクト構成について

こちらの記事が大変参考になったので、 自分なりに噛み砕いた内容をアウトプットしていきたいと思います。

レイヤによる分類

Spring Bootに限らず、開発をしていると様々な機能・目的を持ったクラスを作成する必要が出てきますが、 その際にどのような基準で分類していくかという視点が大事になってきます。

基準となる考え方にはいくつかの種類がありますが、そのうちの一つとして、

の三つによる分類方法があります。

上記の3つのレイヤを基準にしたとき、 入力から出力までのデータの流れは、アプリケーション層→ドメイン層→インフラストラクチャ層の順番になります。

また、各レイヤ間の関係について、

という点についてしっかり理解しておく必要があります。 この理由については後述していきます。

アプリケーション層

Controller¶

リクエストを処理にマッピングし、結果をViewに渡すという画面遷移と、セッション管理。 処理はControlle上で記述せず、ドメイン層のServiceを呼び出す形にする。

View

クライアントへの出力を担う。Spring MVCでは、Viewクラスが該当する。

  1. クライアントからViewへリクエス

  2. リクエストに対応したControllerの処理が行われる

  3. Controllerはドメイン層からServiceの呼び出しを行う。

  4. Serviceから返却された結果をControllerで受け取る

  5. ControllerからViewにその内容を返す。

6.クライアントのブラウザにViewを出力する。

大まかではありますが、このような形で処理が行われます。

Form

これは参考記事からそのまま引用

画面のフォームを表現する。フォームの情報をControllerに渡したり、Contollerからフォームに出力する際に用いられる。 ドメイン層がアプリケーション層に依存しないように、FormからDomain Object(Entity等)への変換や、 Domain ObjectからFormへの変換は、アプリケーション層で行う必要ある。

Helper

Controllerの補助役。

Controllerでは処理のマッピングを行い、具体的な処理そのものはService層を呼び出すと記述したが、 FormからDomain Object(Entity等)への変換や、Domain ObjectからFormへの変換など、ドメイン層が他の層に依存してはいけないといった制約から 例外的にController本来の処理以外の処理を行う必要が生じるケースもある。その時に使われるのがHelper。

Helperが存在することで、Controllerの本来の処理を見通しをよくするといった利点もあるが、 Helperに関しては必ず必要なものとはいえないため、状況に応じて作成するしないの判断を行う。

ドメイン

ドメイン層は、アプリケーションのコアとなる層である。 ビジネス上の解決すべき問題を表現し、 ビジネスオブジェクトや、ビジネスルールを含む(口座へ入金する場合に、残高が十分であるかどうかのチェックなど)。 ドメイン層は、他の層からは疎であり、再利用できる。

前述したとおり、Controlleはクライアントからのリクエストに対応した指示を送るだけで、処理すべき具体的な内容はドメイン層に委譲しています。 Controllerはクライアントからの要望を忠実にドメイン層に伝えますが、伝えるだけ伝えたら、その後の具体的な処理はドメイン層にお願いして、 ドメイン層がうまいことやってくれた結果をクライアントに返すという立ち回りなので、クライアントからの要望にアプリケーションが どのように応えるかというコアの部分はドメイン層で担う必要があるということです。

また、インフラストラクチャ層について、 詳細な部分については後述しますが、先にざっくりと特徴を言ってしまうと、「ドメイン層の実装を持つ層」と言えます。

ここで一旦、先ほど記述した

の部分に立ち戻ります。

ここまで説明した内容から

ドメイン層はビジネス上の問題を解決する上でどのような処理が必要かを意識すべきで、 コントローラからどのように呼び出されるかを意識した設計はすべきでない」

ドメイン層はインフラストラクチャ層に実装される立場にあるので、実装元であるドメイン層が、 実装先であるインフラストラクチャ層に依存するような設計をしてはならない」

ということが言えます。

ドメイン層が他の層に依存してはいけないのは、こういった理由があるからと言えるでしょう。

さて、前置きが長くなってしまいましたが、ドメイン層で扱われるものについて見ていきます。

Domain Object

Domain Objectはビジネスを行う上で必要な資源や、ビジネスを行っていく過程で発生するものを表現するモデル。 大きく分けて、以下3つに分類される。

EmployeeやCustomer, Productなどのリソース系モデル(一般的には、名詞で表現される),
Order, Paymentなどイベント系モデル(一般的には動詞で表現される)、
YearlySales, MonthlySalesなどのサマリ系モデル

データベースのあるテーブルの、1レコードを表現するオブジェクトを表現するEntityは、Domain Objectである。

Repository

Domain Objectのコレクションのような位置づけであり、Domain Objectの問い合わせや、作成、更新、削除のようなCRUD処理を担う。 この層では、インタフェースのみ定義され、実体は、インフラストラクチャ層のRepositoryImplで実装されるため、 どのようなデータアクセスが行われているかについての情報は持たない。

Service

業務処理を提供する。 この処理も、トランザクション境界となる。

Serviceでは、FormやHttpRequestなど、Webに関わる情報を扱うべきではない。 これらの情報は、Serviceの前のApplication層で、ドメイン層のオブジェクトに変換されるべきである。

インフラストラクチャ層

インフラストラクチャ層では、ドメイン層(Repositoryインタフェース)の実装を提供する。 データストア(RDBMSや、NoSQLなどのデータを格納する場所)への永続化や、メッセージの送信などを担う。

RepositoryImpl

RepositoryImplは、Repositoryの実装であり、Domain Objectのライフサイクル管理を隠蔽する。 これにより、ドメイン層がどのようにデータアクセスされているか意識しなくて済む。 Spring Data JPAを使用する場合は、Spring Data JPAが実体を(一部)自動で作成する。

O/R Mapper

データベースとEntityの相互マッピングを担う。 JPAや、MyBatis, Spring JDBCが本機能を提供する。 特に、JPAを用いる場合はEntityManager、MyBatis2(TERASOLUNA DAO)を用いる場合は、QueryDAO, UpdateDAOが該当する。

レイヤ間の依存関係

レイヤ間の結合部は下図のようにインターフェースとして公開する。 こうすることによって各層の実装に依存しない形式とすることができる。

f:id:nikifuna:20191105160732p:plain

参考元より拝借。