MVVM + ヘキサゴナルアーキテクチャ + Entity Framework

WPFアプリケーションではMVVMのレイヤ化アーキテクチャがよく採用される。MVVMはGUIアーキテクチャの1つであり、

  • ビュー層
  • ビューモデル層
  • モデル層

の3層を考える、ビューにフォーカスした考え方である。したがって、例えばDDD(ドメイン駆動設計)におけるレイヤ化アーキテクチャでいうインフラストラクチャ層がMVVMではモデル層の一部になってしまう。つまり、ある程度以上の規模のWPFアプリケーションでは「アーキテクチャとしてMVVMを採用する」というだけでは明らかに不十分であり、MVVMと別のアーキテクチャを組み合わせる必要がある。

今回は以下を組み合わせるとどういう構造になるのかを考える。

f:id:cactuaroid:20181026162806p:plain

ポイントは2つある。

1つ目はMVVMとヘキサゴナルアーキテクチャという2つのレイヤ構造のアーキテクチャの融合について。DIPを適用してインフラストラクチャ層を最上位へ持ってきたレイヤ化アーキテクチャを2層に単純化して見方を変えたものがヘキサゴナルアーキテクチャである。外側が内側を参照するようにする。(内側から外側を参照したい場合はDIPを適用して外側から内側を参照する形にするのが望ましい)

左上はほぼMVVMのままであり、MVVMのモデル層が内側に、ビューモデル層が外側のアダプタ、ビュー層が外側のポートに対応する。モデル層はAPIとしてアプリケーションサービスを公開する。もしこのアプリケーションがGUIの他にWeb APIを提供するなら、Web API用のアダプタ・ポートが別に存在する。

ドメインモデルパターンを採用する場合、内側はさらにアプリケーション層とドメイン層の2層に分けられる。

2つ目はエンティティクラスは内側に属するべきだがEntity FrameworkのDbContextによるDB操作は外側に属するべきである点。あまり考えずに実装するとDbContextがアプリケーション層にまで染み出がちで、レイヤの境界がなくなってしまう。これをリポジトリDIPを適用してリポジトリの実装を外側に配置することで解決する。レイヤ化アーキテクチャでのインフラストラクチャ層→アプリケーション層への参照に対応する外側→内側の参照が現れている。

Entity Frameworkを使う以上、ドメインモデルのエンティティにDBの関心事が染み出してしまうのはある程度避けられないだろう。しかし、Entity Frameworkのエンティティクラスをそのままドメインモデルのエンティティとするのではなく、Entity FrameworkのエンティティクラスをState Objectとして、ドメインモデルのエンティティがこれを参照する形をとることで、Entity Framework(DB)に対する関心事をできるだけドメインモデルから排除することができる。リポジトリがエンティティを保存するときは、内部ではエンティティに対応するState Objectを保存することになる。図中の赤矢印の参照はレイヤ構造として望ましくないが単純さのために妥協している。

なお、Entity Frameworkはリポジトリパターン *1でもCQRSパターン *2でも実装方法が提案されているので、よく検討したい。

参考図書

実践ドメイン駆動設計 (Object Oriented SELECTION)

実践ドメイン駆動設計 (Object Oriented SELECTION)

*1:「実践ドメイン駆動設計」著者によるEntity Frameworkを使用してリポジトリパターンを実装する方法の提案

https://kalele.io/blog-posts/modeling-aggregates-with-ddd-and-entity-framework/

*2:「Programming Entity Framework」著者による、Entity FrameworkでCQRSパターンを実装する方法の提案

https://msdn.microsoft.com/ja-jp/magazine/mt788619.aspx