自定义字段选项是域驱动设计中的值对象还是实体
Is custom field option a Valueobject or an Entity in Domain Driven Design
我正在使用 EF Core,并且我有一个用户可以创建自定义字段然后为该自定义字段创建选项的场景。
public class CustomField : Entity<long>
{
[Required]
public string Name { get; private set; }
public bool IsRequired { get; private set; }
public List<CustomFieldOption> customFieldOptions;
public virtual IReadOnlyCollection<CustomFieldOption> CustomFieldOptions => customFieldOptions;
protected CustomField()
{
}
public CustomField(long id, string name, bool isRequired, List<CustomFieldOption> customFieldOptions)
{
Id = id;
Name = name;
IsRequired = isRequired;
this.customFieldOptions = customFieldOptions;
}
}
public class CustomFieldOption : Entity<long>
{
[Required]
[MaxLength(256)]
public string Text { get; private set; }
protected CustomFieldOption()
{
}
public CustomFieldOption(string text)
{
Text = text;
}
}
public class Client : Entity<long>
{
public Name Name { get; set; }
private List<ClientCustomFieldOptionValue> customFieldOptionValues { get; set; } = new List<ClientCustomFieldOptionValue>();
public IReadOnlyCollection<ClientCustomFieldOptionValue> CustomFieldOptionValues => customFieldOptionValues;
public Client(Name name)
{
}
public Result AddCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
public Result RemoveCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
}
public class ClientCustomFieldOptionValue
{
public CustomFieldOption CustomFieldOption { get; private set; }
protected CustomFieldOptionValue()
{
}
public ClientCustomFieldOptionValue(CustomFieldOption customFieldOption)
{
CustomFieldOption = customFieldOption;
}
}
CustomFieldOption 似乎是一个值对象,因为它包含的文本不需要 Id。但是就存储持久性而言,需要将一个 Id 存储在数据库中不同的 table 上,它可以通过 Id 等进行查询......
我不确定是否应将其添加为实体,因为 ValueObjects 没有 Id。
我遇到的另一个问题是验证。如果它是实体,我如何验证文本 属性。我知道对构造函数进行验证是个坏主意。如果我在 ApplicationLayer 中验证它,那么无论我在哪里创建一个新对象,我都必须验证它不为空和长度。
如果我忘记在其中一个应用程序服务中添加验证并传递空文本,那么我会创建一个不一致的状态。
更新 #1
客户可以 select 自定义字段的一个或多个选项。我想这些需要存储在单独的 table ClientCustomFieldOptionValue 中。在那种情况下,这是一个实体还是一个值对象?那么 CustomFieldOption 呢?它会成为一个实体吗?我很困惑何时使用 Entity 或 ValueObjects
在设计域模型时尽量不要考虑持久性细节。
根据您的描述,CustomFieldOption 表示一个与任何其他结构没有业务关系的个体 属性,因此:
- 它不应包含企业标识符
- 它应该封装自己的验证
意味着它符合值对象的概念(在 ctor 内部验证)。
关于持久性,您的存储库模型应该能够将 CustomFieldOption 对象存储在引用父 table(CustomField 对象)table(具有数据库标识符)的子
在查询方面,存储库应该能够将来自这两个 table 的数据聚合到一个 CustomField 实体中。
(具体如何实现此类 DB 功能取决于您选择使用的 ORM,在您的情况下为 EF)
只是一个观察,如果您将使用 Ef Core 并且包含实体与值对象具有一对多关系,您将有此限制:
Owned types need a primary key. If there are no good candidates properties on the .NET type, EF Core can try to create one. However, when owned types are defined through a collection, it isn't enough to just create a shadow property to act as both the foreign key into the owner and the primary key of the owned instance
如果您使用 DbContext 映射实体和值对象,通常会为值对象定义一个拥有的实体类型或使用记录类型。
对于拥有的实体,这会在您的 table 中创建一个列,如下所示:EntityName_ValueObject(即 Person_Address)但是当您不这样做时,这适用于单个值对象而不是集合'预先知道集合中的项目数。
在设计域时不应该考虑持久性是正确的,但认为具有标识的值对象没有意义也是正确的。
最重要的是,您应该及早意识到这个潜在问题。
我正在使用 EF Core,并且我有一个用户可以创建自定义字段然后为该自定义字段创建选项的场景。
public class CustomField : Entity<long>
{
[Required]
public string Name { get; private set; }
public bool IsRequired { get; private set; }
public List<CustomFieldOption> customFieldOptions;
public virtual IReadOnlyCollection<CustomFieldOption> CustomFieldOptions => customFieldOptions;
protected CustomField()
{
}
public CustomField(long id, string name, bool isRequired, List<CustomFieldOption> customFieldOptions)
{
Id = id;
Name = name;
IsRequired = isRequired;
this.customFieldOptions = customFieldOptions;
}
}
public class CustomFieldOption : Entity<long>
{
[Required]
[MaxLength(256)]
public string Text { get; private set; }
protected CustomFieldOption()
{
}
public CustomFieldOption(string text)
{
Text = text;
}
}
public class Client : Entity<long>
{
public Name Name { get; set; }
private List<ClientCustomFieldOptionValue> customFieldOptionValues { get; set; } = new List<ClientCustomFieldOptionValue>();
public IReadOnlyCollection<ClientCustomFieldOptionValue> CustomFieldOptionValues => customFieldOptionValues;
public Client(Name name)
{
}
public Result AddCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
public Result RemoveCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
}
public class ClientCustomFieldOptionValue
{
public CustomFieldOption CustomFieldOption { get; private set; }
protected CustomFieldOptionValue()
{
}
public ClientCustomFieldOptionValue(CustomFieldOption customFieldOption)
{
CustomFieldOption = customFieldOption;
}
}
CustomFieldOption 似乎是一个值对象,因为它包含的文本不需要 Id。但是就存储持久性而言,需要将一个 Id 存储在数据库中不同的 table 上,它可以通过 Id 等进行查询......
我不确定是否应将其添加为实体,因为 ValueObjects 没有 Id。
我遇到的另一个问题是验证。如果它是实体,我如何验证文本 属性。我知道对构造函数进行验证是个坏主意。如果我在 ApplicationLayer 中验证它,那么无论我在哪里创建一个新对象,我都必须验证它不为空和长度。
如果我忘记在其中一个应用程序服务中添加验证并传递空文本,那么我会创建一个不一致的状态。
更新 #1
客户可以 select 自定义字段的一个或多个选项。我想这些需要存储在单独的 table ClientCustomFieldOptionValue 中。在那种情况下,这是一个实体还是一个值对象?那么 CustomFieldOption 呢?它会成为一个实体吗?我很困惑何时使用 Entity 或 ValueObjects
在设计域模型时尽量不要考虑持久性细节。
根据您的描述,CustomFieldOption 表示一个与任何其他结构没有业务关系的个体 属性,因此:
- 它不应包含企业标识符
- 它应该封装自己的验证
意味着它符合值对象的概念(在 ctor 内部验证)。
关于持久性,您的存储库模型应该能够将 CustomFieldOption 对象存储在引用父 table(CustomField 对象)table(具有数据库标识符)的子
在查询方面,存储库应该能够将来自这两个 table 的数据聚合到一个 CustomField 实体中。
(具体如何实现此类 DB 功能取决于您选择使用的 ORM,在您的情况下为 EF)
只是一个观察,如果您将使用 Ef Core 并且包含实体与值对象具有一对多关系,您将有此限制:
Owned types need a primary key. If there are no good candidates properties on the .NET type, EF Core can try to create one. However, when owned types are defined through a collection, it isn't enough to just create a shadow property to act as both the foreign key into the owner and the primary key of the owned instance
如果您使用 DbContext 映射实体和值对象,通常会为值对象定义一个拥有的实体类型或使用记录类型。
对于拥有的实体,这会在您的 table 中创建一个列,如下所示:EntityName_ValueObject(即 Person_Address)但是当您不这样做时,这适用于单个值对象而不是集合'预先知道集合中的项目数。
在设计域时不应该考虑持久性是正确的,但认为具有标识的值对象没有意义也是正确的。
最重要的是,您应该及早意识到这个潜在问题。