C#でドメイン駆動開発

C#でドメイン駆動設計UIとインフラストラクチャーを実装する方法⑧

前回まででプロジェクトの作成とテストコードの作成Moqを使った外部接触部分のテストの方法を学びました。これからは後回しになっていたユーザーインターフェースと外部データとのアクセス部分であるインフラストラクチャーの部分の実装方法を解説していきます。

UIとインフラストラクチャー

次は,後回しにしていたUIとインフラストラクチャーの部分を作成しましょう。後回しになっていた部分はテストコードでカバーできていない部分ということになります。テストコードでカバーできていないということはコードはできるだけシンプルで少なく実装することが望ましいです。

UI部分の作成

それではまずUI部分を作成します。WinFormプロジェクトに新規フォルダ「Views」を作りましょう。その中に「MeasureView」という名前のFormを作成します。それとは別に「BaseForm」という名前のFormも作成しましょう。デザイン画面にはなにもおかずに画面のコードを表示します。コンストラクタに次のコードを入れておきます。

MeasureViewのデザイン画面を開き,ラベルとボタンを設置します。ラベルのNameは「MeasureValueLabel」,Fontサイズを36くらいにしましょう。ボタンはNameを「MeasureButton」,Textを「Measure」としておきます。

MeasureViewのコードを開き次のように書きます。

MeasureViewModelの生成ではコンパイルエラーになっていると思います。とりあえずおいておきます。

一行目は先ほど作成したBaseFormを継承しています。これで必ず中央に画面表示されるようになります。そのほかにも共通で動作させるようなものをBaseFormに書きます。

コンストラクタにはMeasureViewModelをNewして画面項目にデータバインディングしています。

nameofは少し前の.NETFrameworkだど使えないのでその場合は文字列にしても同様に動作します。

ただこれだとプロパティ名が変更されてもコンパイルエラーにならないので,メンテナンスが少し大変になります。次の関数を作って代用してもよいでしょう。

Utilという静的クラスを作成し,Domain層のHelpersフォルダに入れておきます。MeasureViewからは次のように呼び出します。

これでnameofとほとんど同じ効果が得られます。さて,MeasureViewModel生成のコンパイルエラーを取り除きましょう。現在ViewModelのコンストラクタにはISensorRepositoryの指定を強制しています。

ただISensorRepositoryの具象クラスは存在しておらず,テストではMoqで代用しているだけです。

Infrastructureの実装

Infrastructure層にISensorRepositoryの具象クラスを作りましょう。といっても本当のセンサーに接続するクラスはまだ作りません。これから作るのはFakeといわれるテスト用の値を操作するダミークラスです。Infrastructureに新規フォルダ「Fake」を作ります。

その中に新規クラス「SensorFake」を作りましょう。

アクセス修飾子はInternalです。理由は後述します。ISensorRepositoryを実装しています。これによりfloat GetData()の実装が強制され,MeasureViewModelのprivate変数である_sensorRepositoryにはめ込むことができます。

GetDataの中身は適当なランダム値を返しています。ToSingle()の部分はDomain層のHelpersフォルダにExtensionsという静的クラスを作成し,次の拡張メソッドを追加しています。

Convert.ToSingleと書けばよいのですが,コード量が増えて面倒なのでこうしています。それに文脈的にもvlaue.ToSingle()のほうが頭の構造上自然かと思います。

拡張メソッドはこんな風にピンポイントで便利コード的に定義しています。そしてどのような拡張メソッドがあるかがわかりやすいようにExtensionsという静的クラスにすべて入れるようにしています。といっても拡張メソッドはあまり大量にはならないと思います。大量になるようなら用途ごとにクラス分けも必要かもしれません。

これでISensorRepositoryの具象クラス「SensorFake」が完成しました。しかしアクセス修飾子がInternalなのでWinFormからは見えません。これをpublicにすればWinFormから呼び出せます。

すると次のようなコードになります。

【MeasureView】

これでよいでしょうか?これでは必ずFakeクラスが生成されてしまいます。将来的には「SensorDriver」というセンサーと接続する本番コードを作る想定です。そうなった場合どういうコードになるでしょうか?

画面のコードに書かれていることを置いておいたとしてもこのコードはよくありません。なぜでしょうか?

この書き方だとクライアントコードに知識があります。

Badコードの紹介でも解説しましたが,クライアントコードは知識をもってはいけません。

なぜならクライアントコードが知識を持つと,確実にビジネスロジックが散らばり始めます。

今回の例ではConfiguration.IsFakeの時,つまりFakeモードの時にSensorFakeを生成するという決まり事です。ISensorRepositoryのインスタンス生成は各所で行われる可能性があります。そうなれば他の画面でも同じようにFakeにするかDriverにするかの判断が行われます。UIが判断していいのはUIの事柄だけです。「CheckBoxにチェックが入っているか?」などというUI事情の事柄だけです。UIの事柄はUIで判断するしかありません。しかしシステム全体の決め事はどこか一カ所で行わないとシステムは安定しません。

今はFakeモード,今は本番モードと,1カ所を変えるだけで,アプリケーション全体がその規則にのっとって動作し始めないといけません。

インスタンスの生成はファクトリークラスに任せます。ファクトリーを作るのはこういったインターフェースを実装しているクラスを特定の判断基準で分岐されるために使います。

Factoriesの作成

Infrastuctureの直下に「Factories」というクラスを作りましょう。

ISensorRepositoryのインスタンスの生成はCreateSensorRepositoryのみで行うと決めておきます。

このためにSensorFakeのアクセス修飾子はInternalにしています。WinFormからは決してNewされない,ファクトリーでしかNewできないようにするためです。

分岐が必要になったら,ファクトリーで分岐します。さてそれではCreateSensorRepositoryを呼び出しましょう。どこから呼び出しましょうか?

MeasureViewで次のようにしても呼び出せます。

ただ私はMeasureViewModelに記述したほうがスマートに思います。

MeasureViewModelをなにも指定せず呼び出せばFactoriesによって適切なインスタンスが生成されます。これも使う側にあまり知識を持たせないという意味ではよいと思います。それでは実行してみましょう。ProgramクラスのMain()のなかはつぎのように変更しておいてください。

それでは実行してみましょう。画面が開いたら測定値の初期値は「–」になっていると思います。

「Measure」ボタンを押下するとランダムな値が表示されるはずです。

Measureボタンを押下するとランダムな値が表示される

ドメイン駆動開発

C#プログラマーのための正しい3層構造が分かる「C#アーキテクチャー解説動画」をここで公開しています。よかったら見てみてください。

 

参考図書

ドメイン駆動開発

エリック・エヴァンスのドメイン駆動設計

実践ドメイン駆動設計

ドメイン駆動

現場で役立つシステム設計の原則

エンタープライズアプリケーションアーキテクチャパターン

デザインパターン

オブジェクト指向における再利用のためのデザインパターン

オブジェクト指向のこころ

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本

オブジェクト指向

ジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技
  すばらしい本です。オブジェクト指向が詰まっています。

オブジェクト脳のつくり方―Java・UML・EJBをマスターするための究極の基礎講座
  サンプルコードはjavaですが,最初にオブジェクト指向に目覚めるのに最適な本です。

Head Firstオブジェクト指向分析設計 ―頭とからだで覚えるオブジェクト指向の基本
  マンガみたいな感じで読めて,オブジェクト指向について学べます。  

Clean Code アジャイルソフトウェア達人の技

Clean Architecture 達人に学ぶソフトウェアの構造と設計

Adaptive Code ~ C#実践開発手法

.NETのエンタープライズアプリケーションアーキテクチャ

実装パターン
  廃盤のため異常に高くなりすぎなので,様子を見た方がいい気がします。一万円のボリュームがあるかどうかは微妙です。

オブジェクト指向でなぜつくるのか

レガシーコード改善ガイド

リファクタリング 既存のコードを安全に改善する

C#でオブジェクト指向をする方法

オブジェクト指向の原則1:単一責務の原則