プログラムにはエラー処理が必要です。ドメイン駆動開発とは直接関係はないですが
最後にエラー処理の書き方について解説します。
エラー処理に関して
Save時に今はエラーチェックをしていません。
MeasureValueを空文字で実行するとFormatExceptionが発生します。
シナリオとしては空文字なら
「計測値を入力してください」というメッセージを表示するのが適切です。
次は適切にエラーチェックがされていることのテストコードを書いていきましょう。
MeasureSaveViewModelTestに新しいテストメソッドを作成します。
[TestMethod] public void 計測登録_シナリオ_エラーチェック() { var measureMock = new Mock<IMeasureRepository>(); var viewModelMock = new Mock<MeasureSaveViewModel>(measureMock.Object); viewModelMock.Setup(x => x.GetDateTime()).Returns("2017/01/03 13:00:00".ToDate()); var viewModel = viewModelMock.Object; var ex = AssertEx.Throws<MessageException>(() => viewModel.Save()); ex.Message.Is("計測値を入力してください"); }
途中までは一つ目のテストと同じ内容です。
異なるのはvar ex = AssertEx.Throwsの部分です。
ここではviewModelのSaveを実行したときに,
MessageExceptionが通知されることをテストしています。
その次の行ではその例外のMessageの内容を検査しています。
ここではMeasureValueが初期値の空文字の状態で
Saveを行うとエラー通知が行われるテストをしています。
MessageExceptionは意図していない例外と切り分けるために
独自で作成した例外クラスです。
ドメイン層にExceptionsというフォルダを作成し,
その中に作成します。
MessageExceptionなどの独自で作成した例外は例外レベルを
判断できるようにべって「ExceptionBase」という抽象クラスを継承し,
またその中で「ExceptionType」という列挙体で例外の種類を切り分けています。
namespace DDD.Domain.Exceptions { public sealed class MessageException : ExceptionBase { public MessageException(ExceptionType exceptionType, string message) : base(exceptionType, message) { } }
using System; namespace DDD.Domain.Exceptions { public abstract class ExceptionBase : Exception { public ExceptionBase(ExceptionType exceptionType, string message) : base(message) { ExceptionType = exceptionType; } public ExceptionType ExceptionType { get; } } }
namespace DDD.Domain.Exceptions { public enum ExceptionType { Information, Warning, Error, } }
テストを実行してみます。
NGになります。
MeasureSaveViewModelのSaveメソッドに次のコードを追加します。
public void Save() { Guard.IsNullOrEmptyMessage(MeasureValue, "計測値を入力してください"); var measureValue = Convert.ToSingle(MeasureValue); var entity = new MeasureEntity( Guid.NewGuid().ToString(), MeasureDate, measureValue); _measureRepository.Save(entity); }
Guardクラスは,チェック関連をまとめた静的クラスです。
エラーチェックはいくつかのパターンが決まってくるため,
各クライアントコードでNullチェックなどのif分を書かずに
1行で済ませられるように,
このような静的クラスをまとめています。
Guardクラスはドメイン層のHelpersフォルダに入れています。
このようにビジネスロジックを含まない共通コードはHelpersに入れておきます。
public static void IsNullOrEmptyMessage(string value, string message) { IsNullOrEmptyMessage(value, message, ExceptionType.Information); }
public static void IsNullOrEmptyMessage(string value, string message, ExceptionType exceptionType) { if (string.IsNullOrEmpty(value)) { throw new MessageException(exceptionType, message); } }
これでテストを実行すると成功します。
それではUIとインフラストラクチャを作成しましょう。
Viewsフォルダに「MeasureSaveView」フォームを作成します。
計測日時はDateTimePickerでNameはMeasureDateTextBox,
計測値はTextBoxでNameはMeasureValueTextBox,
label3はNameをUnitLabel,SaveボタンはNameをSaveButtonとします。
画面のコードを開きます。
public partial class MeasureSaveView : BaseForm { public MeasureSaveView() { InitializeComponent(); var viewModel = new MeasureSaveViewModel(); MeasureDateTextBox.DataBindings.Add("Value", viewModel, nameof(viewModel.MeasureDate)); MeasureValueTextBox.DataBindings.Add("Text", viewModel, nameof(viewModel.MeasureValue)); UnitLabel.DataBindings.Add("Text", viewModel, nameof(viewModel.UnitLabel)); SaveButton.Click += (sender, e) => viewModel.Save(); } }
MeasureSaveViewModelの生成でコンパイルエラーが出るため,
以下のコンストラクタを追記します。
public MeasureSaveViewModel() : this(Factories.CreateMeasureRepository()) { }
MeasureFakeのSaveメソッドが未実装のため追記します。
public void Save(MeasureEntity entity) { var index = _entities.FindIndex(x => x.MeasureId == entity.MeasureId); if(index >=0) { _entities[index] = entity; return; } _entities.Add(entity); }
ここでは,すでにMeasureIdが存在している場合は,
値の変更。存在していない場合は追加しています。
最後までお読みいただきありがとうございました。
さらにドメイン駆動を学びたいという勉強家の方は
頭のいい先人の本を是非読んでみてください。
- C#でドメイン駆動開発とテスト駆動開発で保守性の高いプログラムを書く!1
- C#でドメイン駆動をする前に良いコードと悪いコードの定義を理解しよう!2
- C#でドメイン駆動開発をやるうえで採用するアーキテクチャーに関して3
- C#でドメイン駆動開発をするうえで意識べきロジックの2つの種類④
- C#でドメイン駆動開発 アーキテクチャーの実装とテスト駆動での書き方を解説5
- C#でドメイン駆動開発 外部との接触箇所にRepositoryを使う!⑥
- C#でドメイン駆動開発Moqを使ったテスト駆動開発のやり方を解説!⑦
- C#でドメイン駆動設計UIとインフラストラクチャーを実装する方法⑧
- C#でドメイン駆動開発 Entityの書き方と使い方とテスト駆動!⑨
- C#でドメイン駆動開発ValueObjectでプログラムの複雑さを取り除く⑩
- C#でドメイン駆動開発 ViewModelの中に明細項目があるときの実装⑪
- C#でドメイン駆動開発 データ保存時のロジックのMoqの書き方!⑫
- C#でドメイン駆動開発【DDD】エラー処理とExceptionの書き方!⑬
- C#でドメイン駆動開発をするうえで私が参考にした書籍ランキング!