前回はオブジェクト指向の自動化というお話をしました。データベースの列をValueObjectにして,行をEntityにしましょうというお話でした。Repositoryに関しては,外部接触部分をインタフェース化するという事でしたが,Repositoryの具象クラスを作るという考え方もあります。
Repositoryの具象クラスにするケース
データベースから取得した値を,そのままEntityに格納して,ViewModelで使用するだけの場合は具象クラスにする必要はないのですが,例えばデータベースから取得した値を,加工してEntityに入れたい場合などは,Infrastructure層で加工せず,Repositoryの具象クラスで加工したほうがいいです。
Infrastructure層で加工しないほうがいい理由
Infrastructure層で加工しないほうがいい理由は,単純に「テストコードが書き辛い」からです。
Infrastructure層では,実際のSqlServerに接触するため,テストコードからはMoqでタミーの値を流し込みます。なので,Infrastructure層はできるだけシンプルにしておいたほうがいいのです。SQLが複雑であればあるほど,テストコードでテストされていない部分が増えてしまうので,可能な限りSQLはシンプルに実装したほうがいいです。
SQLで取得した値をC#で加工したほうが,融通がきくし,テストコードにかけれるので,バグりにくいプログラムになります。
ViewModelで加工しないほうがいい理由
データベースから取得した値をViewModelで加工してしまうと,画面単位で実装する必要が出てきます。そうなると,ビジネスロジックが各画面にちらばってしまいます。ビジネスロジックはドメイン層に置いておき,だれでも使いたい人が使える状態にしておくのがいい実装です。なので,ViewModelでの加工も極力行わないほうがいいのです。
ドメイン層で加工する方法
以上の理由から,ドメイン層で加工するのがいいのですが,それをどこでするのがいいのか?というお話です。1つの選択しとしてはRepositoryの具象クラスを作るという試みです。例えばIMeasureRepository経由で取得した後に,何か加工したい場合は,MeasureRepositoryという名前のクラスを作ります。
MeasureRepositoryの作成
Domain層のRepositoriesフォルダーを右クリックして,「追加」「クラス」の順で選択し,MeasureRepositoryクラスを作成します。
頭に「I」のついていない,普通のクラスを作ります。
MeasureRepositoryのコーディング
次のように実装します。
using NDDD.Domain.Entities; namespace NDDD.Domain.Ripositories { public sealed class MeasureRepository { private IMeasureRepository _repository; public MeasureRepository(IMeasureRepository repository) { _repository = repository; } public MeasureEntity GetLatest() { var val1 = _repository.GetLatest(); System.Threading.Thread.Sleep(1000); var val2 = _repository.GetLatest(); System.Threading.Thread.Sleep(1000); var val3 = _repository.GetLatest(); System.Threading.Thread.Sleep(1000); var val = (val1.MeasureValue.Value + val2.MeasureValue.Value + val3.MeasureValue.Value) / 3; return new MeasureEntity(val3.AreaId.Value, val3.MeasureDate.Value, val); } } }
IMeasureRepositoryを宣言し,コンストラクタでIMeasureRepositoryを設定します。このクラスの中で,IMeasureRepositoryのGetLatestを呼び出し,好きなように加工します。このようにすると,テストコードにかけることができます。今回の例では,GetLatestを3回呼び出して,平均を取得するという処理です。このような処理はSQLでやろうとすると難しい処理です。SQLではあまり無理せず,C#で加工したほうがいいです。
ViewModelの変更
MeasureRepositoryを具象クラスにしたので,LatestViewModelは次のように変更します。
using NDDD.Domain.Entities; using NDDD.Domain.Ripositories; using NDDD.Infrastructure; using System; using System.ComponentModel; namespace NDDD.WinForm.ViewModels { public class LatestViewModel : ViewModelBase { //private IMeasureRepository _measureRepository; private MeasureRepository _measureRepository; private string _areaIdText = string.Empty; private string _measureDateText = string.Empty; private string _measureValueText = string.Empty; public LatestViewModel() : this(Factories.CreateMeasure()) { } public LatestViewModel(IMeasureRepository measureRepository) { _measureRepository = new MeasureRepository(measureRepository); } (省略) public void Search() { var measure = _measureRepository.GetLatest(); AreaIdText = measure.AreaId.DisplayValue; MeasureDateText = measure.MeasureDate.DisplayValue; MeasureValueText = measure.MeasureValue.DisplayValue; } } }
IMeasureRepositoryを宣言していた部分をMeasureRepositoryに変更し,コンストラクタでは,引数のIMeasureRepositoryを利用してMeasureRepositoryを生成しています。 SearchメソッドのGetLatestは具象クラスのGetLatestが呼び出されるので,加工された値(3回取得した平均の値)が取得できます。 これで,データベースからの値取得後のビジネスロジックを共通化できます。
#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_さいごに