ドメイン エンティティをモデル化するときは、現実世界への影響を考慮するのが最善です。Employee
あなたがエンティティを扱っているとしましょう。
従業員には名前が必要です
現実の世界では、従業員には常に名前が必要です。従業員が名前を持たないことは不可能です。つまり、従業員の名前を指定せずに従業員を「構築」することはできません。したがって、パラメーター化されたコンストラクターを使用してください。また、従業員の名前は変更できないこともわかっているため、プライベート セッターを作成することで、これが起こらないようにしています。.NET 型システムを使用して従業員を検証することは、非常に強力な検証方法です。
public string Name { get; private set; }
public Employee(string name)
{
Name = name;
}
有効な名前にはいくつかの規則があります
今、それは面白くなり始めています。名前には一定のルールがあります。単純なルートを取り、有効な名前は null または空ではないものであると仮定しましょう。上記のコード例では、次のビジネス ルールは検証されません。この時点で、まだ無効な従業員を作成できます。セッターを修正して、これが決して起こらないようにしましょう。
public string Name
{
get
{
return name;
}
private set
{
if (String.IsNullOrWhiteSpace(value))
{
throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
}
name = value;
}
}
個人的には、コンストラクターよりもプライベート セッターにこのロジックを含めることを好みます。セッターは完全に見えないわけではありません。エンティティ自体はそれを変更する可能性があり、有効性を確保する必要があります。また、常に例外をスローしてください。
IsValid()
なんらかの形式のメソッドを公開するのはどうですか?
上記のEmployee
エンティティを取ります。メソッドはどこでどのようにIsValid()
機能しますか?
無効な従業員の作成を許可し、開発者がチェックでその有効性を確認することを期待しIsValid()
ますか? これは脆弱な設計です。知らないうちに、名前のない従業員がシステムを巡回し、大混乱を引き起こします。
しかし、おそらく名前検証ロジックを公開したいと思いませんか?
制御フローの例外をキャッチしたくありません。壊滅的なシステム障害の場合は例外です。また、コードベースでこれらの検証ルールを複製したくありません。したがって、おそらくこの検証ロジックを公開することは、それほど悪い考えではありません (ただし、それでも最高というわけではありません!)。
あなたができることは、静的IsValidName(string)
メソッドを提供することです:
public static bool IsValidName(string name)
{
return (String.IsNullOrWhiteSpace(value))
}
プロパティが多少変更されます。
public string Name
{
get
{
return name;
}
private set
{
if (!Employee.IsValidName(value))
{
throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
}
name = value;
}
}
しかし、このデザインには何か怪しいものがあります...
エンティティの個々のプロパティの検証メソッドを生成し始めています。プロパティにあらゆる種類のルールと動作が関連付けられている場合、おそらくこれは、そのプロパティの値オブジェクトを作成できる兆候です!
public PersonName : IEquatable<PersonName>
{
public string Name
{
get
{
return name;
}
private set
{
if (!PersonName.IsValid(value))
{
throw new ArgumentOutOfRangeException("value", "Person name cannot be an empty value");
}
name = value;
}
}
private PersonName(string name)
{
Name = name;
}
public static PersonName From(string name)
{
return new PersonName(name);
}
public static bool IsValid(string name)
{
return !String.IsNullOrWhiteSpace(value);
}
// Don't forget to override .Equals
}
これで、Employee
エンティティを単純化できます (null 参照チェックは除外しました)。
public Employee
{
public PersonName Name { get; private set; }
public Employee(PersonName name)
{
Name = name;
}
}
クライアントコードは次のようになります。
if(PersonName.IsValid(name))
{
employee = new Employee(PersonName.From(name));
}
else
{
// Send a validation message to the user or something
}
それで、私たちはここで何をしましたか?
ドメイン モデルが常に一貫していることを確認しました。かなり重要。無効なエンティティは作成できません。さらに、値オブジェクトを使用して、さらなる「リッチさ」を提供しています。 PersonName
により、クライアント コードの制御と機能が強化され、簡素化されEmployee
ました。