🌟

続・とにかくドメイン駆動設計を実践してみる試み ~機能追加編~

2022/02/27に公開

はじめに

本記事は「とにかくドメイン駆動設計を実践してみる試み ~TODO管理システム編~」の続きです。

サービスは立ち上げて終わりではなく、様々な要望を受けて少しずつ進化していきます。
そこで、ドメイン駆動設計を採用して立ち上げたサービスに機能追加や仕様変更が発生したらどうなるのか、いまいち想像できなかったので実際に手を動かして確認してみることにしました。

仮想の課題を設定する

プロジェクトオーナーから「TODOをいつまでに消化すれば良いか分からない」という課題が上がりました。

そこで関係者からのヒアリングを行った結果以下のような意見が出ました。

  • TODOをいつまでに完了すれば良いかを表す日時を設定できると良い
  • TODOによっては作業に期間が必要なものがある
  • 必ずしも作業期間完了予定日を設定したいわけではない
  • TODO作成後に作業期間や期日などを変更することもある

用語と仕様を決定する

議論の中で出てきた「いつまでに完了すれば良いかを表す日時」、「期日」とか「作業期間」、「完了予定日」など似たような言葉を整理してきます。

上記の議論を通して最終的に用語と仕様を以下のように決定しました。
合わせて英語での表現も決めておくとコーディング時に統一しやすそうです。

用語

用語 英語表記 説明
開始日 BeginDateTime TODOに手を付ける日時。入力しなくても良い
期日 DueDateTime TODOを完了する日時。入力しなくても良い

仕様

  • 開始日は期日よりも前の日時を設定する
  • 開始日、期日共に設定しなくても良い
  • 開始日のみまたは期日のみ設定することもできる
  • ステータスにかかわらずTODO作成後に変更することができる

オブジェクト図を修正して課題解決できるか確認

決定した用語と仕様をオブジェクト図に反映して、関係者と一緒に確認をします。
赤字が今回追加・変更した個所です。
今回はTODO集約に変更が入ることになりそうです。


修正後

ドメインモデル図を修正する

オブジェクト図を使って課題を解決できそうか確認出来たらドメインモデル図も修正します。
赤字箇所が追加・変更した属性とルールです。


修正後

コードにドメインモデルへの修正を反映する

ドメインオブジェクトの修正

TODOドメインオブジェクトに開始日、期日を表現するプロパティとルールを実装します。

TODOドメインオブジェクトの修正(抜粋)
Todo.cs
 public class Todo
 {
+	public DateTime? BeginDateTime { get; private set; }
+	public DateTime? DueDateTime { get; private set; }

	public void Edit(
		TodoTitle title,
		TodoDescription? description,
+		DateTime? beginDateTime,
+		DateTime? dueDateTime)
	{
		if (IsDeleted)
		{
			throw new DomainException("削除したTODOを編集することはできません。");
		}

+		if (beginDateTime > dueDateTime)
+		{
+			throw new DomainException("開始日は期日より前になるように設定してください。");
+		}

		Title = title ?? throw new DomainException("タイトルを設定してください。");
		Description = description;
+		BeginDateTime = beginDateTime;
+		DueDateTime = dueDateTime;
		UpdatedDateTime = DateTime.Now;
	}
}

テストの追加と修正

ドメインオブジェクトの修正に合わせて、テストも修正します。
以下はTodoTestの修正を抜粋しています。
ここには載せていませんが、ユースケースやリポジトリのテストも修正します。

TODOドメインオブジェクトのテスト(抜粋)
TodoTest.cs
[Test]
public void 開始日を期日より後ろに設定して作成しようとすると例外が発生する()
{
    // 実行・検証
    Assert.That(
	() => Todo.CreateNew(
	    title: new TodoTitle("タイトル"),
	    description: new TodoDescription("詳細"),
	    beginDateTime: DateTime.Now.AddDays(7),
	    dueDateTime: DateTime.Now,
	    ownerId: new UserId(Guid.NewGuid().ToString("D"))),
	Throws.TypeOf<DomainException>());
}

[Test]
public void 開始日を期日より後ろに設定して編集しようとすると例外が発生する()
{
    // 準備
    var todo = TodoGenerator.Generate(
	title: "タイトル",
	description: "説明文",
	beginDateTime: DateTime.Now,
	dueDateTime: DateTime.Now.AddDays(7));
    var newTitle = new TodoTitle("新しいタイトル");
    var newDescription = new TodoDescription("新しい説明文");
    var newBeginDateTime = DateTime.Now.AddDays(7);
    var newDueDateTime = DateTime.Now;

    // 実行・検証
    Assert.That(
	() => todo.Edit(newTitle, newDescription),
	() => todo.Edit(
	    title: newTitle,
	    description: newDescription,
	    beginDateTime: newBeginDateTime,
	    dueDateTime: newDueDateTime),
	Throws.TypeOf<DomainException>());
}

テストが通るようにユースケースなどを修正

一通りテストを書いたら、それらのテストが通るようにユースケースやリポジトリの実装を修正します。

プレゼンテーション層の修正

最後にプレゼンテーション層を修正して、開始日や期日を設定、表示できるようにして完了です。

デモ


新機能実装後のデモ

機能追加、仕様変更の流れ

今回手を動かしてみて、現段階では以下のような流れで修正していくのが良いのかなと思いました。

実際のチーム開発では実装前にFigmaなどで実装後の画面イメージを作成してそれを見ながら話し合ったり、大きな機能なら実装途中の段階でも関係者に触ってもらったりすると良さそうです。

機能追加、仕様変更の流れフローチャート

感想

システムのコアであるドメイン層への変更がユースケース層、プレゼンテーション層、インフラ層に波及していく様子が確認できました。

影響範囲が広くてもどこに影響が出るのか明確ですし、影響が出ないとされている箇所もテストによって保障されているので安心感があります。

また、エンジニア以外のメンバーも機能追加や仕様変更による影響を可視化して一緒に議論できるので作業量の見積もりや用語に関するすれ違いも減らせそうです。

ソースコード

今回の修正による差分は以下で確認できます
GitHub - 差分

この記事に対応したコードはタグ付けしてあります
GitHub - 記事に対応したバージョン

最新はこちらから確認できます
GitHub - tatsuteb/todo-management-system

参考文献

前回の記事と同じなのでリンクだけ貼っておきます
とにかくドメイン駆動設計を実践してみる試み ~TODO管理システム編~ - 参考文献

最後に

今回は記事を書くのにちょうど良い粒度の課題を設定して実践してみました。
今後も勉強しつつTODO管理システムを更新していこうと思います。
記事にするかはわかりませんが、そこそこ大きい更新をしたらTwitter(@tatsuteb)に流す予定です。

Discussion