【失敗事例に学ぶDDD】
MVCパターンではDBと計算ロジックは分離すべし

記事イメージ

お疲れ様です。堺です。

MVC(モデル・ビュー・コントローラー)パターンでは、Ruby on Rails等の有名フレームワークにおいてM(モデル層)はニアリイコールでデータベーススキーマ=ORマッパの受け皿という紹介をされます。

そのため「モデル層はDBと密結合してよい」という誤解を生みがちですが、それはモデルのもう一つの機能「計算」を無視することになり後々の問題の火種となります。

今回は、筆者が過去その誤解のもとダメコードを生産してしまった失敗経験と、そこから学ぶソフトウェア設計のプラクティスを共有したいと思います。

モデル層には、ORマッパの受け皿の他に「独自の計算」の役割がある

Ruby on Rails等のFWではモデル層はDBのカラムと一致したプロパティを持たせ、RDBの各行をインスタンス化する役割を担います。

他方、MVCアーキテクチャにおいてそのアプリが専門とする計算(コアドメイン)を担うのもモデル層です。

例えばこれから事例とする財務計算システムでは、入力となる財務情報をつかって独自の計算を行い財務指標を出力するといった役割をいいます。こういったものは、通常はモデル層に収めるべし、というのがMVCの基本的考え方です。

財務情報を入力し、独自計算した財務指標を出力するモデル(クラス)が今回紹介する事例の中心となる機能です。
財務情報を入力し、独自計算した財務指標を出力するモデル(クラス)が今回紹介する事例の中心となる機能です

財務計算システム開発で、計算ロジックとDBを密結合させて失敗した事例を紹介

上述の通り、事例として私が過去に作った、財務データを元に一定のロジックで計算して結果を表示・DB格納するシステムを取り上げます。(今後公表予定のプロダクトなので詳細はその時まで伏せますが、今回の議論には支障ありません。)

次の図が、私が当初組んだ構成図です。尚、このシステムは、ASP.NET Coreを使用したMVCアーキテクチャで組まれたWebアプリケーションです。

全体の構成図。データベースと財務計算モデルが結びついているところが後々の失敗に繋がります。
全体の構成図

MVC初心者がやりがちな「ファット・コントローラー」(全ロジックをコントローラーに詰め込むアンチパターン)にはなっていない分まだマシです。

しかしながら、財務計算モデルはDBと結合し、計算過程でDBからロードしたり、逆に一部の成果物を計算途中でDBに登録し別の箇所で再度ロードするといった動きをしています。この構成が、後々実際に面倒な問題を引き起こしました。

この財務計算ロジック、他の環境でも役立ちそうなのにDBから引き剥がせない

このシステムが完成してから数年後、中核となっている財務計算モデルを他の箇所でも使用したいという要望がありました。新たな利用環境は、Webアプリではなくデスクトップ上でスタンドアロンで起動するアプリケーションです。

そこで改めてコードを見てみたところ、私はまずい問題に気付きます。コアである財務計算の最中にDBにアクセスする前提のコードが混ざっているため、DBと計算ロジックを引き剥がせないのです。

こうなると、DBがない環境に持ち出すためには、そのロジック自体にも影響が出る可能性もある大手術が必要になってしまいます。このケースでも、かなり処理の流れを変えつつ取り出すことにしたので、相応の工数を必要としました。

本来関係ない「計算ロジック」と「DB」をコード上で結合させたのが最大のミス

財務計算モデルは現実における課題解決を目指したソリューションであり、本来はDBもブラウザーもない世界に存在するモデルであるはずです。

しかしながら、今回の事例では全く関係ない計算ロジックとDBを密結合させて実装した点が失敗でした。

ソフトウェアの構成は、コアドメインとなる中核の計算ロジックを動かすために、コアに関係ない多量のコード、インフラ、UIを必要とするのが通常です。

そういった区分(コアか否か)を明確に行った上、コアとその他の部分が疎結合となるように慎重に分離しておかなければ、何かを変更するたびに大工事が必要となってしまうのです。

失敗を踏まえ構成を再検討。DBとの結合箇所を再構成する。

冒頭でも書いた通り、モデル層の役割はORマッパとコア計算ロジックの二つです。

この内の計算ロジックに関してはDB(インフラ)ともビューとも分離されている必要とがあります。よって計算ロジック内でDBにアクセスする箇所は全て削除し、コントローラーから処理のためのデータを受け取り、コントローラーに値を返すだけのシンプルなクラスに再構築します。

再構築前のダメな構成。財務計算モデルがDBと密結合しているので、この環境の外に持っていけません。
再構築前のダメな構成
再構築後の構成。財務計算モデルは副作用を除去してコントローラーとだけやり取りします。DB操作もコントローラーに委託します。この構成なら、例えば入力をJSONなどデータ交換可能な形式にすれば、あらゆる環境から財務計算モデルを計算可能になります。
再構築後の構成

DDDにおける「ドメインを隔離する」という考え方

DDD、ドメイン駆動設計で有名ないわゆるエヴァンス本(邦訳:エリック・エヴァンスのドメイン駆動設計、他)では、ドメイン(ここではアプリがそのアプリたる中核機能を意味します)を隔離せよ、と主張されています(第4章)。

今回の例では財務計算モデルこそがコアとなるドメインであり、DBやビューから慎重に隔離されることが必要だったのです。

変更に強くするためには、コアは隔離される必要がある

今回の例のように、アプリの使途が完成後に微調整されることや、あるいはコアドメインにとって重要なファクタ・不要な機能が完成後に見つかることが非常に多いという事実は、ソフトウェアエンジニアリングの世界に広く知られた経験則です。

それを裏付けるように、エヴァンス翻訳本でも次のような一説があります。

重大な発見はいつでも、設計や実装をするために努力する際に現れるからだ。非常に特殊で予期せぬ問題というものが常に発生する。あらかじめ作られるモデルは、的外れな対象を掘り下げる一方で、重要な問題を見過ごしてしまうのである。

引用:エリック・エヴァンスのドメイン駆動設計 第3章

DDDの話題に限らず変更に強くするためには、各モジュールは責務ごとに疎結合にしておくことが重要と言われています。

DBなどそのドメインロジックに不要な要素と密結合して不都合が生じた今回の事例をみれば、その妥当性は明らかと言えます。

尚、この議論はいわゆる「関心の分離」または「責務の分離」というソフトウェア設計上の考え方に共通します。関連記事を書いていますので、是非合わせてご参照ください。

関連記事:【関心の分離/責務の分離】変更に強いプログラミング実践手法

今回の話はDDDの一部でしかないが、重要な部分

DDDの考え方はソフトフェア設計全般に及ぶ上に階層的となっており、この記事で全体を表現しきることはできません。

しかしながらエヴァンス本において、DDDを駆動する3原則として真っ先に「コアドメインに集中すること」と挙げられています。

それほどにコアドメインとは何たるか、いかに作りこむか、そしてどのように取り扱う(隔離する)べきかが重視されていることを推察させます。

記事筆者へのお問い合わせ、仕事のご依頼

当社では、IT活用をはじめ、業務効率化やM&A、管理会計など幅広い分野でコンサルティング事業・IT開発事業を行っております。

この記事をご覧になり、もし相談してみたい点などがあれば、ぜひ問い合わせフォームまでご連絡ください。

皆様のご投稿をお待ちしております。

記事筆者へ問い合わせする

※ご相談は無料でお受けいたします。
IT活用経営を実現する - 堺財経電算合同会社