NDDD

ドメイン駆動開発_フォルダー構成編_#03_依存関係

ここまでで,プロジェクトを4つ作りましたが,今回はそれらのプロジェクトの依存関係をどのように設定するかというお話をしていきたいと思います。

依存関係とは?

依存関係とは,どのプロジェクトが,どのプロジェクトを参照するか?ということで,参照するというのは,他のプロジェクトの機能を使えるようにするか?という事です。

例えば,Domainの機能をInfrastructureで使いたければ,InfrastructureでDomainのプロジェクトを参照しておく必要があります。どれであればすべてのプロジェクトを参照すればいいのでは?と思うかもしれませんが,そういうわけにもいきません。参照関係とは,1方向にしか行えないので,InfrastructureがDomainを参照した場合,DomainからInfrastructureは参照できなくなります。なので,参照関係をどのように設定するか?というのは,最初に決める,非常に重要な設定となります。ちなみに,双方向で参照しようとすると,C#は循環参照というエラーでそのような設定ができないことを通知します。

双方向の参照関係が設定できないのであれば,わざわざプロジェクトを分けずに,1つのプロジェクトにしてしまえば,すべての機能をすべてのクラスで使えていいのではないか?と思うかもしれません。しかしそれも賢明な選択とは言えません。

プロジェクトを分けるにはそれ相応の意味があります。プロジェクトを1つにしてすべてのプログラムコードを書いてしまうと,すべてのコードが至る所で使用されている可能性があるため,可読性が落ちます。プロジェクトを分けていれば,例えばInfrastructureがDomainを参照し,Domainがどこも参照していない場合は,DomainではInfrastructureやWinFormの機能を使用していないことが明確になります。WinFormにWindowsフォームにまつわる機能しか実装していない場合,いつの日かWPFに変更する際も,WinFormのプロジェクトだけを取り換えれば動作するようになります。すべての機能を1つのプロジェクトに入れていては,どこまでがWindowsフォームに依存するコードかが,判断できないため,WPFの移行が非常に大変な作業になります。

WindowsフォームからWPFへの移行など,時代の変化とともに進化していくテクノロジーと,純粋なC#のビジネスロジックを分離してコーディングすることで,時代の変化に振り回されずにシステム開発をしていくことが,プロジェクトを分離する狙いです。

参照関係

それでは具体的な参照関係を説明します。

まず,「Domain」プロジェクトはどこも参照しません。このプロジェクトは最上位に位置し,どこのプロジェクトにも依存しない,純粋なC#のビジネスロジックの集まりとなります。

続いて,「Infrastructure」プロジェクトは「Domain」プロジェクトを参照します。

「WinForm」プロジェクトは「Domain」プロジェクトと「Infrastructure」プロジェクトを参照します。

単体テストプロジェクトは「Domain」プロジェクト,「Infrastructure」プロジェクト,「WinForm」プロジェクトのすべてのプロジェクトを参照します。

各種プロジェクトの意味

依存関係は前述した通りですが,ここで,各プロジェクトがどのような意味を持つのかを,整理しておきます。

Domainプロジェクト

Domainプロジェクトはどこのプロジェクトも参照せず,純粋にプレーンなC#のコードのみをコーディングします。この部分にビジネスロジックを記述します。ビジネスロジックとは,アプリケーションで独自に決める仕様です。そのアプリケーションで表現したいことです。そのアプリケーションで行う計算や,判定ロジックをここに集めます。ビジネスロジックとは,アプリケーション独自のロジックなので,何らかの事情で仕様が変更されたときにロジックを変更する必要があります。その際に,どこに書いているのか?というのが,即時にわかるべきだし,また,同じロジックがいろんな場所に散らばっていてはいけないのです。

例えば消費税の計算なんかは1か所でやるべきだし,各画面に散らばっていて,いろんなところを変更して,「一部5%のままで残っていました」なんてことがあってはならないのです。どこかの1か所を変えれば,すべてが変更されるようになっているべきだし,自信をもって変更できるような,わかりやすい場所に記述されているべきです。

こういった感じで,ビジネスロジックとは,アプリケーションで表現するべきロジックを集中して記述していきます。どのようにクラスを分割するのかは,後述するので,楽しみにしておいてください。

Infrastructureプロジェクト

Infrastructureプロジェクトは,外部との接触部分を記述します。例えばSqlServerとかオラクルデータベース,ファイルへのアクセス,PCとの通信等です。自分が作成しているアプリケーションの外側へアクセスする部分のみを記述します。

なぜ外部との接触部分をInfrastructureプロジェクトに記述するかというと,外部の変化に振り回されずにコーディングをするためです。

外部のシステムは,外部の事情で変化します。例えば,SqlServerはSqlServerの勝手なタイミングでバージョンアップします。その際に,今まで使えていた関数が無くなったり,メモリリークの問題が発見されて,何か処置するコーディングを入れないといけなくなったりするかもしれません。そういったマイクロソフトの事情以外にも,我々の仕様の事情で,列を追加したり,文字数を減らしたり,増やしたり,カラム名を変更するかもしれません。そういったときに,プログラムの至る所で,SqlServerに接触していると,修正すべき箇所というのが,非常に分かりづらくなります。

すべての箇所を修正できたのかも,判断できないようになるのです。例えば,購買システムのアプリケーションを作成して,100画面くらいがSqlServerにそれぞれアクセスしていると,本当に必要な個所をすべて修正できたかを確認するのは,非常に大変です。

ですから,そういった外部の事情で変更されるようなものは,1か所にまとめてアクセスし,1か所でケアできるような構造にするのが望ましいという事になります。

だからInfrastructureプロジェクトではSqlServerにアクセスする部分やファイルにアクセスする部分などを,局所的に実装することで,外部の変化に振り回されずにコーディングをしていきます。また,このような実装をすることで,プログラムとしても見通しが良くなります。

あとは,それ以外にも,外部との接触部分というのは,テストコードからテストしづらいという特性があります。コードでテストする場合に,外部のシステムとの連携部分は,ダミーのデータを流し込む等の工夫が必要です。そういった意味でも,外部接触部分はプロジェクト単位で切り離しておいたほうが,実装がしやすくなります。このあたりのテストコードの書き方も後述しますので,楽しみにしておいてください。

WinFormプロジェクト

このプロジェクトも,前述した通り,時代の変化によって変化が必要な部分です。例えばWindowsフォームからWPFに変更したくなった場合に,変更が必要な部分だけをWinFormに集めておけば,その部分のみWPFに置き換えれば,InfrastructureとDomainは一切の変更が不要で,そのまま使用することができます。なので,このプロジェクトはWinFormをWPFに変更する際に,書き換えないといけない機能のみを実装する,という感覚で感じてもらえればいいかと思います。

実演

それでは,実際に依存関係の設定をしていきましょう。

Domainプロジェクト

Domain プロジェクトはどこも参照しないため,今回は何もしません。

Infrastructureプロジェクト

InfrastructureプロジェクトはDomainプロジェクトを参照するため,参照の追加をします。Infrastructureプロジェクトの「参照」を右クリックして,「参照の追加」をクリックします。

次のように,参照マネージャー画面が表示されるので,左側で「プロジェクト」を選択した状態で,「NDDD.Domain」にチェックを付けてOKボタンを押下します。これで参照の追加は完了です。

WinFormプロジェクト

次にWinFormプロジェクトに参照追加を同じ要領で行います。WinFormプロジェクトの参照を右クリックして,「参照の追加」をクリックします。

参照マネージャー画面で「NDDD.Domain」と「NDDD.Infrastructure」にチェックをいれて,OKボタンを押下します。

単体テストプロジェクト

最後に単体テストプロジェクトに参照を追加します。「参照」を右クリックして,「参照の追加」をクリックします。

参照マネージャーで「NDDD.Domain」と「NDDD.Infrastructure」と「NDDD.WinForm」にチェックをいれて,OKボタンを押下します。

以上で参照設定は完了です。これで,InfrastructureからDomainの機能を使えるようになり,WinFormプロジェクトから,DomainとInfrastructureの機能が使えるようになりました。単体テストプロジェクトからは,すべての機能が使えるようになっています。

今後は,この設定のなかで,どのようにクラスやフォルダーを配置していくかを解説していきます。

NDDD

#01_プロジェクトの作成

#02_プロジェクトの追加
#03_依存関係
#04_ドメイン駆動開発でApplication層は必要?
#05_Domainのフォルダー構成
#06_Infrastructureのフォルダー構成
#07_WinFormのフォルダー構成
#08_Testsのフォルダー構成
#09_テスト駆動で実装するための事前準備
#10_テストコードとViewModelの追加
#11_テストコードを追加する
#12_ Repositoriesフォルダーの作成
#13_ Entitiesフォルダーの作成
#14_ Mockの作成
#15_フォーム画面の作成
#16_画面のコントロールデータバインドする
#17_Fakeを使ってタミーデータを画面に表示させる
#18_Fakeデータを画面に通知する
#19_PropertyChangedの方法を変更する
#20_Fakeとデータベースの値を切り替える方法
#21_Sharedクラスを作成する
#22_クラスを生成するファクトリークラスを作る
#23_#if DEBUGでFakeデータがリリースされないようにする
#24_DEBUGモードであることをわかりやすくしておく
#25_Factories以外から生成できないようにしておく
#26_Factoriesの呼び出しはViewModelで行う
#27_外部の設定ファイルの値で判断する
#28_Fakeデータを切り替える方法
#29_FakePathを設定ファイルとSharedに移す
#30_Fakeデータのバリエーション
#31_Shareクラスの活用方法
#32_ベースフォームを作る
#33_SharedにログインIDを記憶する
#34_BaseFormでログインユーザーを表示する
#35_ValueObject
#36_ValueObjectを作成する
#37_抽象クラスValueObjectを使用してイコールの問題の解消
#38_AreaIdにビジネスロジックを入れる
#39_AreaIdクラスをEntityに乗せる
#40_MeasureDateの作成
#41_MeasureValueの作成
#42_オブジェクト指向の自動化
#43_Repositoryの具象クラス
#44_例外処理
#45_例外の作成
#46_インナーエクセプション
#47_例外の欠点
#48_メッセージの区分
#49_エラー処理の共通化
#50_ログの出力
#51_タイマー処理はどこに置く?
#52_タイマークラスの作成
#53_StaticValues
#54_Logics
#55_Helpers
#56_Module
#57_トランザクションはどこでかける?
#58_特徴を見極める
#59_さいごに