ドメインモデル駆動開発をやってみる(2)

ドメインモデルと言えるかは?の点はありますが、前回伝言メモユースケースをモデル化して実装してみます。

ユースケースのモデル化

ユースケースをモデル化する方法として直ぐに思いつくのが、アクティビティ図やフローチャートで、.NETであればWFを使って実行可能なモデルを作成できます。ただ単純に低レベルのコンポーネントを使ってWFを作成するとモデルというよりはプログラミングになってしまいます。このため、より抽象度の高いビジネス上の意味のあるアクティビティを定義してワークフローを作成してみます。このアクティビティはコーヒーブレイクテストのレベルの粒度をイメージしています。
コーヒーブレイクテストとはユースケースの一連の作業のうち各作業の間にコーヒーブレイクができるかどうかを考え、ブレイクできないものは1つの塊として考えるようにすることです。コールバックや確認はコーヒーブレイクできないので1つの塊として考え、それどれコールバックアクティビティと確認アクティビティとして表現します。

WFを使うとタイムアウト処理や並列処理などプログラムで直接記述することが難しいことが本当に簡単に記述でき実装できてしまいます。上記のアクティビティでもDelayを使ってコールバックが必要な伝言メモがある一定時間たつとメールを送信するような処理を記述しています。

ユースケースのシナリオは、この抽象化されたアクティビティを利用して記述するとかなりユースケースのシナリオのモデルに近くなります。

伝言の受け付けを、今回ユースケースのトリガーとして扱ったのでWFに明示しなかったのですが、WFをビジネスフローとしてみるのであればWFのアクティビティとしてあったほうが良いように後から感じています。

ちなみに、WF上でビジネスルールを含めることができます。


ユースケース・コントローラ

ユースケースのフローをさらに抽象的にすればシナリオを動作させるユースケース・コントローラとして考えることができます。これはフローの中身ではなく関連オブジェクトの協調動作をビューにした抽象化です。伝言とその状態を情報、確認(Confirm)やコールバック(CallBack)の通知のための操作を持っているというレベルで取り扱えます。画面がユースケースの具体的な実装に依存しないようにできます。

public class MessageUseCaseController
{
    public Message PhoneMessage { get; private set; }
    public MessageWorkflowStatus CurrentStatus { get; private set; }
    public WorkflowInstance Instance { get; private set; }
    private PhoneMessageService WorkflowService;

    public MessageUseCaseController(
    ...

    public void Confirm()
    {
        WorkflowService.OnConfirmed(Instance);
    }

    public void CallBack()
    {
        WorkflowService.OnCalledBack(Instance);
    }

}


ユースケースコントローラの利用

アプリの実装はユースケースコントローラを利用すればシンプルです。伝言のタイムアウトの処理も正しく実装できています。(テストでSleepを使うのはどうかはありますがとりあえずこの方がイメージがつきやすいのでこのままにしておきます)

[TestMethod]
public void TestCallBack()
{
    phoneMessage = new Message();
    phoneMessage.Action = ResponseAction.CallBack;
    phoneMessage.CallBackPhoneNo = "06-666-6666";
    phoneMessage.ReceiptPerson = new Employee() { Name = "yamada taro" };
    phoneMessage.TargetPerson = new Employee() { Name = "suzuki hanako" };
    phoneMessage.CallerPerson = new CustomerCaller(null, new Customer(), "Yamamoto");

    var useCase = _localService.EntryMessage(phoneMessage);

    _scheduler.RunWorkflow(useCase.Instance.InstanceId);
    Assert.AreEqual(MessageWorkflowStatus.WaitCallBack, useCase.CurrentStatus);

    useCase.CallBack();

    _scheduler.RunWorkflow(useCase.Instance.InstanceId);
    Assert.AreEqual(MessageWorkflowStatus.Done, useCase.CurrentStatus);
}

[TestMethod]
public void TestTimeout()
{
    ...

    var useCase = _localService.EntryMessage(phoneMessage);

    _scheduler.RunWorkflow(useCase.Instance.InstanceId);
    Assert.AreEqual(MessageWorkflowStatus.WaitCallBack, useCase.CurrentStatus);

    System.Threading.Thread.Sleep(10000);

    _scheduler.RunWorkflow(useCase.Instance.InstanceId);
    Assert.AreEqual(MessageWorkflowStatus.Done, useCase.CurrentStatus);
}


ブレークスルー

WFを使うと通常ではタフなユースケースコントローラの実装も次元が異なるレベルで今回簡単になりました。モデルがそのまま動くというメリットは本当に大きいです。