この記事は、ドメイン駆動設計 Advent Calendar #1 の16日目の記事です。
Eric Evansのドメイン駆動設計(以後Evans本)第2部の第4章で触れられている
「利口なUI」についてまとめています。
なぜこれをピックアップしたかと言うと、アンチパターンから入ったことで、
ドメイン駆動設計というものがだいぶしっくりくるようになったためです。
利口なUI
利口なUIは、ユーザーインターフェイスにビジネスロジックが入っているパターンで
Smart UIとも呼ばれており、Evans本には唯一アンチパターンという言葉が添えられて紹介されています(もちろん他にもアンチパターンはいくらでもあります)。
利口なUIと言う呼ばれ方がされているのは、UI層が責務を持ちすぎている意味が込められています。
Eric Evansは「アンチパターンと紹介することに対して皮肉を効かせている。」
と言っていますが、この言葉自体がかなり皮肉染みていると思います。
ここでのUIはViewやテンプレートエンジン、フロントエンドやスマホアプリなどで、
この外部アクターとしては、人だけではなく該当するインターフェースを利用した他のサービスだったりすることもあります。
例.勤怠ソフトの場合
勤怠ソフトで利口なUIの極端な例を見ていきます。
とある勤怠ソフト
以下のような要件のソフトがあったとします。
- 従業員が1日の勤怠を入力すると、勤怠をフロントエンドにてリアルタイムでひと月の合計時間を計算、表示する。
- 従業員の操作により保存ボタンを押してサーバーサイドに勤怠データを送り、保存する。
利口なUIではデータベースはただの共有リポジトリ
以下の図の通り、ビジネスロジックはUIで行われます。
Controllerと呼んで良いのか微妙なところですが、
Controllerがデータの保存などを担う1レイヤーなシステムです。
この時、Controllerはただのデータの架け橋で、データベースは画面間のただの共有リポジトリになります。
もしこれが複雑な画面ではなく単純な画面のシステムの場合、
ひたすらControllerとUIを量産して、それぞれの画面の担当者がデータベースに正しく値が入るように実装していくケースでは、もしかしたら問題なくプロジェクトを達成できるかもしれません。
ロジックの複製
ここで新しく給与画面を追加したとします。
勤怠と給与の世界はやっかいです。
給与計算になると1週間ごとの勤怠が登場します。
(1週間40時間もしくは44時間を超える場合は、時間外労働の割増計算が必要になるため。)
勤怠画面はひと月の勤怠実績の集計を確認したいだけなので、1週間ごとの勤怠は勤怠画面に必要ないとします。
その場合、給与画面で1週間ごとの勤怠を集計するために再び勤怠の集計が必要になってきてしまいます。
勤怠画面と給与画面をつなぐ共有できる勤怠集計のロジックはなく、データベース上の1日ごとの勤怠レコードのみが2つの画面をつないでいます。
スケールに限界が出てくる
サービスが拡大し、「スマホアプリ版を作りましょう。」となることは珍しくありません。
もともとそんな想定じゃなくても、ユーザーの要望や市場、ビジネスモデルの変化などでサービスは常にスケールするしコアドメイン自体も勤怠から給与に変わるかもしれません。
勤怠や給与の場合は法律・制度だって変わっていきます。
スマホアプリ版を作ることになったとします。
この場合、スマホアプリにほとんど同じロジックを複製しなければなりません。
スマホアプリでもiOSとAndroidでさらにロジックの複製が増えるかもしれません。
同じようなロジックを各クライアントやview、テンプレートに作り始めプロダクトをイテレートしていくには限界が出てきます。
「利口なUI」のメリット、デメリットについて
Evans本からメリット・デメリットを抜粋させていただきます。
(Eric Evans. エリック・エヴァンスのドメイン駆動設計. 翔泳社.)
メリット
- 単純なアプリケーションの場合、生産性が高く、すぐに作れる。
- それほど有能でない開発者でも、この方法なら、ほとんど訓練しないで仕事ができる。
- 要求分析が不足していても、プロトタイプをユーザに公開し、その要望を満たすように製品を変更することで、問題を克服できる。
- アプリケーションが互いに分離しているので、小さなモジュールの納品スケジュールは比較的正確に計画できる。
- 単純なふるまいをつけ加えるようなシステムの拡張であれば、容易に対応できるだろう。
- 関係データベースはうまく機能し、データレベルでの統合が実現される。
- 4GLツールが実にうまく機能する。
- アプリケーションが引き継がれた場合、保守プログラマは自分が理解できない部分を素早く作り替えられる。変更による影響が、それぞれ特定のユーザインタフェースに限定されるからだ。
解釈次第ですが、メリットを見ていても正直「これ、本当にメリットか?」というものばかりです。
まとめてみるとデータベース自体はただの共有リポジトリでしかないため、ロジックを変更しても他の画面に影響することはないので開発自体は横展開しやすい。
生産性が高く、技術者も低レベルでOK、要求分析不足しても大丈夫。
画面数が多く単純なアプリケーションで、ウォータフォールかつ上流工程から下流工程まで階層的に分かれている開発体制に向いているかもしれません。
プロダクトのイテレートが無いことを願うしかありません。
デメリット
- アプリケーションの統合は困難で、データベースを経由させるしかない。
- ふるまいが再利用されることも、ビジネスの問題が抽象化されることもない。ビジネスルールは、適用先の操作それぞれで複製されることになる。
- 迅速なプロトタイピングやイテレーションを行おうとしても、自然と限界に行き当たる。抽象化が欠けているために、リファクタリングの選択肢が制限されるからだ。
- 複雑さによってすぐに覆い尽くされてしまうので、成長しようとしても、単純なアプリケーションを追加することしかできない。
- より豊かなふるまいが実現できるようになるといった、優雅な道は存在しない。
データベースが唯一の共有リポジトリなので画面間つなぐのはデータベースになってしまうため、ロジックなどの共有はできず、イテレーションがし辛い。
どう見ても冗長的です。
DDDでの解決手段
レイヤーを増やすことによりドメインを隔離する
Evans本に紹介されている4レイヤーアーキテクチャーや最近話題のクリーンアーキテクチャー、増田さんがよく紹介されている3レイヤー+ドメインモデルアーキテクチャーのように層を増やすことでEvans本第4章本題のドメインを隔離する話につながってきます。
3レイヤー+ドメインモデルアーキテクチャーの場合
ドメイン層を切り離し、ドメイン知識を集約していくことで高凝集・低結合になりました。
責務がそれぞれ明確になり、リファクタリングのポイントも見つけやすくなります。
他の4レイヤーアーキテクチャーやクリーンアーキテクチャーでもそれぞれ特性はあるものの、同じようなことが言えるかと思います。
その他の解決手段
トランザクションスクリプト
UIをアプリケーションから切り離すことでUIが行っていた一連の流れをアプリケーション側で手続き的に処理します。
ただ、オブジェクトモデルは持たないので、いろいろな箇所にビジネスロジックが散らばってしまったり、Fat ControllerやFat Modelが誕生したりサービスが全てを振る舞うようになってしまいドメインモデル貧血症につながってきます。
Universal Javascript
UIでもプレゼンテーション層とドメイン層の2層に分け、フロントエンドとサーバーサイドでドメイン層を共有できればうまくいく解決するかもしれません。
現状だとnode環境が必要になってくるので、動作環境に依存してしまっている感じはあります。
最後に
今回は極端な例でしたが、ifの判断条件やループのリスト取得にビジネスロジックがテンプレートエンジンに入ってくることはある程度成熟したプロダクトでもよく見かけます。
Evans本では利口なUIを選択するかMDDを選択するかという感じで書かれていますが、大抵の場合はアプリケーションの負債としてすでに利口なUIが存在している場合がほとんどだと思います。
一部のスタンドアローンな業務アプリケーションに適用はできるかもしてませんが、イマドキのWebアプリケーションやスマホアプリの時代にはあまり適している場面は多くないはずです。
UIに責務が寄り過ぎてしまっていると言うだけで、これはUIに限った話ではありません。
ちょうどアドベントカレンダーで@mtoyoshiさんが書いていただいた記事(ドメインモデルにView固有の事情が入ってくることの対策)は今回とは逆のViewの責務がドメインモデルに寄ってしまう問題でした。
結局はどこにどの責務をもたせるべきか、どのようにすれば高凝集、低結合な状態を作り出せるかということが重要なんだと思います。
こうした怪しいパターン身につけることで問題や負債に対する嗅覚を上げていきたいです。
次回のドメイン駆動設計 Advent Calendar #1 は@k-okinaさんです。