ドメインとかモデルとかよく分かってない

MVC とか DDD とかの中で、「ドメイン」とか「モデル」とか「サービス」とか「ロジック」とかいう言葉が何となく出てきて、ちゃんと区別出来てない感じがしたので整理する。

目次

MVC の整理

まず、一般的な MVC の定義を再度整理する。

ココで、「Model とはドメイン層のこと」という言葉が出てくることで、理解がややこしくなる。

DDD の文脈

MVC の構成要素とは別に、オブジェクト指向や DDD の文脈で、「ドメイン」とか「ドメインモデル」とかいう言葉が使われる。無理に MVC の「M」に当てはめようとせず、一旦分離して考えた方が良い。

「モデル」を表現するパターンとして、以下の3つのパターンがある。

(ドメインイベントは無視。DB アクセスを担う DAO も無視)

ディレクトリ構成でいうとどうなるの

普通に MVC 構成で分けようと思うと、

my-app/
├ views/
├ controllers/
└ models/

となって、models にあらゆるビジネスロジックが集約することになる。model という単語が「ドメイン層」と同義な印象である。

Rails のディレクトリ構成を超簡素に抽出するとこんな感じか。

my-app/
├ views/
├ controllers/
├ models/
├ entities/
├ services/
└ value-objects/

Rails の場合は ActiveRecord パターンがあるためか、modelsentities を担っていてあんま区別されてない感じ。Rails 以外の言語・FW で、例えば Web API を構築する際は、DDD の文脈ではなく「O/R マッパーとしての entities」と models とを分けて管理すると良さそうだ。

「ドメイン層」を表現するディレクトリ階層がないものの、modelsentitiesservicesvalue-objects の4つをまとめて「ドメイン層」と表現することになるワケだ。無理やり階層で表現するとしたらこうなるか。

my-app/
├ views/
├ controllers/
└ domains/
   ├ models/
   ├ entities/
   ├ services/
   └ value-objects/

自分は Node.js 系 SPA だとこんな感じで作るかしら

フロントエンド (SPA) の場合、上述までの views/ の中を細分化するワケだが、自分は Angular の構成が気に入っているので、Angular が推奨する構成をベースに考えがち。

frontend/
├ core/ … (アプリ全体にまたがるモノ)
│ └ services/
├ shared/ … (アプリ全体で共用するモノ)
│ ├ classes/    … (共通のクラス)
│ ├ components/ … (共通コンポーネント置き場)
│ └ services/   … (共通サービス置き場)
└ pages/ … (いわゆる「Views」)
   ├ hoge-page/
   └ fuga-pages/
      ├ classes/    … (1画面内で使うクラス)
      ├ components/ … (1画面内で使う子コンポーネント置き場)
      └ services/   … (1画面内で使うサービス置き場)

フロントエンドにおけるビジネスロジックは、ほとんど Service として表現する。Service で扱う「モデルクラス」、Value Object などは、明確に分離しなくても良いレベルなので classes/ ディレクトリでまるっと管理してしまうことが多いか。別けたければココらへんは Angular の設計思想に沿って別けたらよかろう。

Components → Services という呼び出し順は一定しているので、見通しは悪くないと思う。

バックエンドは、Angular に似た設計思想で作られている NestJS を参考にすると良いだろう。以前は Rails の構成を参考に、Express.js で同等の構成を作っていたこともあった。

backend/
├ routes/      … (ルーティング・URL 定義)
├ controllers/ … (リクエストを受け取りレスポンスするだけ)
├ services/    … (ドメインサービス。基本的にコントローラからサービスを呼び出してあれこれ処理させる作りにしがち)
├ models/      … (ドメインモデル。FW や ORM に依存せず、プロパティとメソッドを持つクラス)
└ entities/    … (TypeORM などの ORM と連携するためのエンティティ。「ドメインモデル」とほぼ同じだけど ORM 用のクラスが出来がち)

NestJS だと、機能ごとに modules/ みたいなディレクトリの配下に切り出してるかな。

Routes → Controllers → Services という動線は常に一定させておいて、Services 内で適宜 Entities を使って処理して、Models に結果をまとめて返す、みたいなことをしがち。models/ が微妙な感じで、classes/ でも良いような、Value Object じみた使い方をしてることが多い。

分かったような分かんないような

自分はそもそも DDD やクリーンアーキテクチャといった手法が好きじゃないので、ドメインとかモデルとかいう単語が別の文脈の似て非なる用語と混ざっていながら、どうでもええわと思って無視してきた。

並のプログラマのスキルじゃ、Service クラスを適宜作って分かるレベルまでしか理解できんって。なんでもかんでも小さな Value Object や細かい Model を作っても、やたら見通し悪いなーと思うだけで、ついていけんって。

だから自分は、Components or Controllers → Services という動線で基本的に終わらせて、DB アクセスが必要なら適宜 (ORM としての) Entity を作ったり、データをまとめる単位としてテキトーに POJO 的な Class (「モデル」クラス) を作ればええやん、という感じでいる。

そして、一つのアプリが担う「ドメイン」を小さく保ち、マイクロサービス構成で組み合わせていけば、大きな「ドメイン」も表現できるし、チーム開発する時はそういう感じになるんじゃね?なんて思っている。DDD とかクリーンアーキテクチャって、ちょっとモノリシックな匂いがするのもあって嫌いなのよね。やたらに膨らまそう膨らまそうとしてるでしょ?俺たちそんな賢くないから、小さく保とうや。

参考文献