私は自分自身をDDDの専門家とは考えていませんが、ソリューションアーキテクトとして、可能な限りベストプラクティスを適用しようとしています。私は、DDDのノー(パブリック)セッター「スタイル」の賛否両論について多くの議論があることを知っており、議論の両側を見ることができます。私の問題は、スキル、知識、経験が非常に多様なチームで働いているということです。つまり、すべての開発者が「正しい」方法で作業することを信頼することはできません。たとえば、オブジェクトの内部状態の変更がメソッドによって実行されるがパブリックプロパティセッターを提供するようにドメインオブジェクトが設計されている場合、誰かがメソッドを呼び出す代わりにプロパティを設定することは避けられません。この例を使用します。
public class MyClass
{
public Boolean IsPublished
{
get { return PublishDate != null; }
}
public DateTime? PublishDate { get; set; }
public void Publish()
{
if (IsPublished)
throw new InvalidOperationException("Already published.");
PublishDate = DateTime.Today;
Raise(new PublishedEvent());
}
}
私の解決策は、プロパティセッターをプライベートにすることです。これは、オブジェクトをハイドレートするために使用しているORMがリフレクションを使用してプライベートセッターにアクセスできるためです。ただし、単体テストを作成しようとすると問題が発生します。たとえば、再発行できないという要件を検証する単体テストを作成する場合、オブジェクトが既に発行されていることを示す必要があります。確かに2回Publishを呼び出すことでこれを行うことができますが、私のテストでは、最初の呼び出しでPublishが正しく実装されていると想定しています。それは少し臭いようです。
次のコードを使用して、シナリオをもう少し現実的にしましょう。
public class Document
{
public Document(String title)
{
if (String.IsNullOrWhiteSpace(title))
throw new ArgumentException("title");
Title = title;
}
public String ApprovedBy { get; private set; }
public DateTime? ApprovedOn { get; private set; }
public Boolean IsApproved { get; private set; }
public Boolean IsPublished { get; private set; }
public String PublishedBy { get; private set; }
public DateTime? PublishedOn { get; private set; }
public String Title { get; private set; }
public void Approve(String by)
{
if (IsApproved)
throw new InvalidOperationException("Already approved.");
ApprovedBy = by;
ApprovedOn = DateTime.Today;
IsApproved = true;
Raise(new ApprovedEvent(Title));
}
public void Publish(String by)
{
if (IsPublished)
throw new InvalidOperationException("Already published.");
if (!IsApproved)
throw new InvalidOperationException("Cannot publish until approved.");
PublishedBy = by;
PublishedOn = DateTime.Today;
IsPublished = true;
Raise(new PublishedEvent(Title));
}
}
以下を検証する単体テストを書きたい:
- ドキュメントが承認されない限り公開できません
- ドキュメントを再発行できません
- 公開されると、PublishedBy値とPublishedOn値が適切に設定されます
- 公開されると、PublishedEventが発生します
セッターへのアクセスなしでは、オブジェクトをテストの実行に必要な状態にすることはできません。セッターへのアクセスを開くと、アクセスを防止する目的が無効になります。
この問題をどのように解決しますか?