如何使用聚合根内部数据库中的数据填充子实体?

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 对工厂的依赖变得明确,这很好。

请注意,在实体中具有服务依赖性通常会得出这样的结论:最好通过工厂创建或重构实体。这样,工厂可以在构建特定实体时提供特定实体所需的所有服务。永远记住 对象构造是一个实现细节,客户不需要知道它就可以使用对象。