NDDD

ドメイン駆動開発_フォルダー構成編_#43_Repositoryの具象クラス

前回はオブジェクト指向の自動化というお話をしました。データベースの列を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回取得した平均の値)が取得できます。 これで,データベースからの値取得後のビジネスロジックを共通化できます。

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_さいごに