WPFアプリケーション層 詳細
さてWPFアプリケーション層のレイヤー分割についてですが、パターンAにするかパターンBにするか悩ましいところです。
まず、プレゼンテーションとドメインを分離(Presentation Domain SeparationつまりPDSを実現)するために、MVVMモデルを採用します。WPFは強力なバインディング機構をもちます。そのWPFアプリケーションでPDSを実現するために考えられたのが、MVVMモデルです。このためWPFとMVVMは非常に相性が良いパターンです。WPFでアプリケーションを構築するにあたり、あえて他のパターンを採用する理由もないため、MVVMモデルを採用することとしました。
ここまでは問題ありません。問題はModel層です。
私は通常は左のパターンAを採用するケースが多いです。これは一つのユースケースの実現に、二つ以上の画面が登場することを普段は想定しているからです。
一つのユースケースが複数の画面にまたがる場合、ユースケースの状態(情報)を管理するにはどうすれば良いか?という問題とユースケース内でロジックを共有化するのに、Usecaseレイヤーがあった方が都合が良いことが多いからです。
しかし本システムでは一つのユースケースが複数の画面によって構成されることは現時点では想定されていません。またユースケース固有の複雑なロジックも存在しません。非常にシンプルなマスター管理のCRUDが発生するのみで、それらは基本的にWCFによるサーバーサイドの実装に含まれます。
そこで本システムではパターンBを採用することとしました。
なおService Client層は、WCFサービスとの通信レイヤーです。実際にはWCFのシグニチャを定義したinterfaceを用いて、ChannelFactoryから自動生成するため実装は必要ありません。この点がWCFの開発生産性が高い所以です。
WCFサービス層 詳細
WCFサービスは、次のレイヤー構成にて実現します。
Service Host層は、WCFサービスの実装をホスティングする層になります。WCFをホスティングする仕組みには、二つの代表的な仕組みがあります。
- Windows Service上でのセルフホスティング
- IIS上でのホスティング
これらは機能的には大きな差異はありません。運用する環境において、Windows ServiceとIISのいずれでホスティングした方が、運用が容易になるかという側面で検討すると良いと思います。
今回のサンプルの実装においては、Windows 10上だとIISでホスティングする実装は難しいため、Windows Serviceとして実装しています。これはWindows 10上のLocal IISでは統合Windows認証が利用できず、IIS ExpressではHTTP・HTTPS以外のプロトコルが利用できず、IISのホスティングを利用する場合は別途Windows Serverが必要になってしまうことから、サンプル実装としては向かないという判断によるものです。
続いて、Service Implementation層です。これはWCFとして公開されるサービスの実装レイヤーになります。トランザクション管理を含むビジネスロジックが実装されます。実際のSQLの発行はDatabase Access層に移譲します。
ところでWeb上のWCFのサンプル実装を見ると、Service Host層とService Implementation層が一つに統合されている例が多く見られます。しかしその手法を取った場合、Windowsサービスでホストしていると劇的に開発生産性が落ちます。
開発時はWindowsサービスに登録するモジュールは、ビルド先のbinフォルダに配置されたものを登録したいと考えるでしょう。しかしそうした場合、Windowsサービスを止めないとビルドがエラーとなります。Windowsサービスが実行モジュールのファイルハンドルをロックしているためです。かと言って、Windowsサービスに登録するモジュールを別フォルダに置いたとしても、実行する際には毎回サービスを止めてファイルをコピーする必要があります。またWindowsサービスをデバッグするには、プロセスにアタッチする必要があります。F5を押して終わりという訳にはいきません。
また単純に、ユニットテストの対象コードと非対象コードが混在してしまうため、テストの終了判断が難しくなるという問題もあります。
これらの理由から、まずService Host層とService Implemantation層を分離します。その上で運用時はWindows Serviceに、実装時はWindowsコンソールアプリケーションにService Implemantation層をホストすることで全ての課題は解決することができます。
詳細はGitHubをご覧ください。
最後に、Database Asscess層ではデータベースへのSQLの発行と、結果のオブジェクトへの変換を実装します。トランザクション管理はService Implementation層で行うため、Service Implementation層で作成されたConnectionオブジェクトを利用して、データベース処理を実装します。