如何使用聚合根内部数据库中的数据填充子实体?
How do I fill a child entity with data from DB from inside an aggregate root?
我有一个聚合根 Entity
,它在请求时进行一些处理。如果在该处理过程中满足条件,则必须初始化 SubEntity
。问题是 SubEntity
还有一个子实体 Status
必须使用来自数据库的起始值进行初始化。
我正在努力严格遵循 SOLID 和 DDD 原则,但我是新手。我现在的工作方式是使用工厂,但对我来说它看起来不对,因为我不喜欢让消费者 class 为这个工厂服务的想法(为了遵循 DIP), 因为这是该实体中域逻辑的一部分。
我这样做对吗?这是设计此类 classes 的正确方法吗?我有什么选择?
public class Entity
{
public virtual SubEntity SubEntity { get; private set; }
public void Process(int someData, ISubEntityFactory subEntityFactory)
{
if (SomeConditionIsMet)
{
SubEntity = subEntityFactory.Create(this);
}
}
}
public class SubEntity
{
public SubEntity(Entity entity, Status status)
{
Entity = entity;
Status = status;
}
public virtual Entity Entity { get; private set; }
public virtual Status Status { get; private set; }
}
public class Status
{
public const int StartingId = 1;
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<SubEntity> SubEntity { get; private set; }
}
public class SubEntityFactory : ISubEntityFactory
{
// property and constructor omitted
public SubEntity Create(Entity entity)
{
var status = UnitOfWork.StatusRepository.GetByID(Status.StartingId);
return new SubEntity(entity, status);
}
}
解决方案
基于 theDmi 的出色回答,我决定接收一个 initialStatus
变量作为 Process
方法的参数,因此我的域不与数据库耦合。然后我验证 initialStatus
以确保它的 id
匹配 Status.StartingId
。之后,连工厂都不要了,看起来干净多了。
public class Entity
{
public virtual SubEntity SubEntity { get; private set; }
public void Process(int someData, Status initialStatus)
{
ValidateInitialStatus(initialStatus);
if (SomeConditionIsMet)
{
SubEntity = new SubEntity(this, initialStatus);
}
}
private void ValidateInitialStatus(Status initialStatus)
{
if (initialStatus == null)
{
throw new ArgumentNullException("initialStatus");
}
if (initialStatus.Id != Status.StartingId)
{
throw new ArgumentException("Initial status is invalid");
}
}
}
public class SubEntity
{
public SubEntity(Entity entity, Status status)
{
Entity = entity;
Status = status;
}
public virtual Entity Entity { get; private set; }
public virtual Status Status { get; private set; }
}
public class Status
{
public const int StartingId = 1;
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<SubEntity> SubEntity { get; private set; }
}
你们工厂里的 UnitOfWork.StatusRepository.GetByID(Status.StartingId)
好像很可疑。尽量避免将工厂与存储库耦合(尽管反过来也可以,例如在重构期间使用工厂)。
一个干净的解决方案如下:
public class Entity
{
private readonly ISubEntityFactory _subEntityFactory;
public Entity(ISubEntityFactory subEntityFactory) {
_subEntityFactory = subEntityFactory;
}
public void Process(int someData, Status initialStatus)
{
if (SomeConditionIsMet)
{
SubEntity = _subEntityFactory.Create(this, initialStatus);
}
}
}
这导致 initialStatus
必须由调用应用程序服务检索。我想你想避免这种情况,但它比将域耦合到 DB(我会不惜一切代价避免)要干净得多。
如果可以,请将 initialStatus
重新设计为值对象。我不知道你的情况是否可行,但这会使设计更加稳健。
此外,不要将 subEntityFactory
作为参数传递。工厂是一种特殊的服务,因此它应该被注入到构造函数中而不是四处传递。这使得 Entity
对工厂的依赖变得明确,这很好。
请注意,在实体中具有服务依赖性通常会得出这样的结论:最好通过工厂创建或重构实体。这样,工厂可以在构建特定实体时提供特定实体所需的所有服务。永远记住 对象构造是一个实现细节,客户不需要知道它就可以使用对象。
我有一个聚合根 Entity
,它在请求时进行一些处理。如果在该处理过程中满足条件,则必须初始化 SubEntity
。问题是 SubEntity
还有一个子实体 Status
必须使用来自数据库的起始值进行初始化。
我正在努力严格遵循 SOLID 和 DDD 原则,但我是新手。我现在的工作方式是使用工厂,但对我来说它看起来不对,因为我不喜欢让消费者 class 为这个工厂服务的想法(为了遵循 DIP), 因为这是该实体中域逻辑的一部分。
我这样做对吗?这是设计此类 classes 的正确方法吗?我有什么选择?
public class Entity
{
public virtual SubEntity SubEntity { get; private set; }
public void Process(int someData, ISubEntityFactory subEntityFactory)
{
if (SomeConditionIsMet)
{
SubEntity = subEntityFactory.Create(this);
}
}
}
public class SubEntity
{
public SubEntity(Entity entity, Status status)
{
Entity = entity;
Status = status;
}
public virtual Entity Entity { get; private set; }
public virtual Status Status { get; private set; }
}
public class Status
{
public const int StartingId = 1;
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<SubEntity> SubEntity { get; private set; }
}
public class SubEntityFactory : ISubEntityFactory
{
// property and constructor omitted
public SubEntity Create(Entity entity)
{
var status = UnitOfWork.StatusRepository.GetByID(Status.StartingId);
return new SubEntity(entity, status);
}
}
解决方案
基于 theDmi 的出色回答,我决定接收一个 initialStatus
变量作为 Process
方法的参数,因此我的域不与数据库耦合。然后我验证 initialStatus
以确保它的 id
匹配 Status.StartingId
。之后,连工厂都不要了,看起来干净多了。
public class Entity
{
public virtual SubEntity SubEntity { get; private set; }
public void Process(int someData, Status initialStatus)
{
ValidateInitialStatus(initialStatus);
if (SomeConditionIsMet)
{
SubEntity = new SubEntity(this, initialStatus);
}
}
private void ValidateInitialStatus(Status initialStatus)
{
if (initialStatus == null)
{
throw new ArgumentNullException("initialStatus");
}
if (initialStatus.Id != Status.StartingId)
{
throw new ArgumentException("Initial status is invalid");
}
}
}
public class SubEntity
{
public SubEntity(Entity entity, Status status)
{
Entity = entity;
Status = status;
}
public virtual Entity Entity { get; private set; }
public virtual Status Status { get; private set; }
}
public class Status
{
public const int StartingId = 1;
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<SubEntity> SubEntity { get; private set; }
}
你们工厂里的 UnitOfWork.StatusRepository.GetByID(Status.StartingId)
好像很可疑。尽量避免将工厂与存储库耦合(尽管反过来也可以,例如在重构期间使用工厂)。
一个干净的解决方案如下:
public class Entity
{
private readonly ISubEntityFactory _subEntityFactory;
public Entity(ISubEntityFactory subEntityFactory) {
_subEntityFactory = subEntityFactory;
}
public void Process(int someData, Status initialStatus)
{
if (SomeConditionIsMet)
{
SubEntity = _subEntityFactory.Create(this, initialStatus);
}
}
}
这导致 initialStatus
必须由调用应用程序服务检索。我想你想避免这种情况,但它比将域耦合到 DB(我会不惜一切代价避免)要干净得多。
如果可以,请将 initialStatus
重新设计为值对象。我不知道你的情况是否可行,但这会使设计更加稳健。
此外,不要将 subEntityFactory
作为参数传递。工厂是一种特殊的服务,因此它应该被注入到构造函数中而不是四处传递。这使得 Entity
对工厂的依赖变得明确,这很好。
请注意,在实体中具有服务依赖性通常会得出这样的结论:最好通过工厂创建或重构实体。这样,工厂可以在构建特定实体时提供特定实体所需的所有服务。永远记住 对象构造是一个实现细节,客户不需要知道它就可以使用对象。