DDD

ドメインモデルにView固有の事情が入ってくることの対策

はじめに

DDDではよく「コアドメインに集中する」ということが言われますが、これは視点を変えればいかにそうではないものたちを適切に分離するか、という話でもあります。
例えば一例としてDDDと[レイヤード|ヘキサゴナル|クリーン]アーキテクチャを組み合わせた設計では、ドメインモデルとHTTPやRDBMSといった固有の技術的な処理をするクラスは層を隔てて分離する、といった設計を見ることが出来ます。

分離する話でいうと、プレゼンテーション層、つまりView部分との分離はというと、JSP等の技術で表現していた時代とは違ってSPAスタイルが定着して以降はサーバーサイドからは物理的に切り離されるようになりました。
しかしながら、案外Viewの事情がドメインモデルに入ってくるということは今でも起きているのではないかと感じています。

なぜ起きるのか、その対策について自分たちのプロジェクトでのケースで書いてみたいと思います。

Viewの事情がドメイン層に入ってくるとき

どんな時に入ってきやすいのかを確認するために、例としてこんな仕様があったら、を考えてみたいと思います。

  • 開発対象のアプリケーションはWEBとスマホアプリに対応しているとします
  • 個人毎の設定ページより以下の2つのアプリケーションカスタム設定がおこなえるとします
    • アプリケーションのテーマカラーの変更ができる
    • アプリケーションの文字フォントの変更ができる
  • なお、この設定はブラウザおよびスマホ共通で反映される必要があります

さて、このデータはどこに持つのが良いでしょうか。

複数のViewを横断した永続化が必要となるとLocalStorageなどは使えないので、クライアントサイドからするとサーバーにリクエストしてDBに格納してもらう必要がありそうです。
そこでサーバーサイド担当のエンジニアは事情を汲んで適切と思われる場所を考え、その結果ユーザーごとの設定情報だからということでドメイン層のUserクラスを拡張、例えばこんな感じの設計になりがちではないでしょうか。

user.scala
case class User(
  id: UserId,
  name: UserName,
  ...
  setting: UserSetting,
  ...
)

case class UserSetting(
  themeColor: ThemeColor,
  font: Font
)

テーマカラーやフォントというのは、いかにもViewっぽい雰囲気を出したかっただけのサンプルなのですが、ここでのポイントはView横断で保存する必要が出た時にフロントエンドからするとサーバーサイドに依頼するしか選択肢もなく、、というのが入り込んできやすい原因かと考えています。

今はWEB画面とスマホアプリの両方を提供するサービスも多いのでこういうシーン多くなってきてませんか・・?

私たちのプロジェクトにおける対策

フロントエンドチームで自由に使えるDBを持つこと、です。

なお、実際のところは対策をした、というよりは使えるDBがあって自然とそういう方向に進んでいった、というのが正確なところです。

背景はこうです。今のプロジェクトではまさに上で述べたようにWEB画面とスマホアプリを提供していて、通知処理等の関係からFirebaseを導入しました。
このFirebaseにはDatabaseがあり、javascriptやswiftからこのDBを操作することが出来ます。
ブラウザにさくっと保存したいデータはLocalStorageに、ブラウザ・アプリを横断したようなデータはFirebase DBにと使い分けられています。この選択肢ができたことでサーバーサイドエンジニアと設計について話す時もView固有のデータかドメイン由来のデータなのかを議論する文化が出来ました。
そうしてView固有なデータの永続化責務はフロントエンドチーム内に閉じるようになっていきました。

Firebaseじゃなくても

なお、Firebaseを導入しなくても、例えばGCPだとCloud FunctionやGoogle App EngineでNode.jsを使って簡単にWEBサーバーとDBアクセス処理を書くことができるようになってきているので、View用DBを持つことの敷居はかなり低くなっていると言えます。

おわりに

サーバーサイドエンジニアな自分的にはDBは我々の管理下にある、という思い込みがあったので対策としてのこの発想に至らなかった、という点が自分的には新しかったポイントでした。

仮にサーバーサイドで処理するとしても、異質だと分析しているのに無理してドメインモデルにマッピングしてRepository経由でDBに保存するくらいなら、Controllerで依頼を受けてDAO経由でDBに保存、ユースケース層・ドメイン層には持ち込まないほうがベターに思います。