我需要一些关于 DDD 上一直困扰的问题的建议。
我有一个域模型,它是聚合根
public class Objective {
public int ObjectiveId { get; private set; }
public string ObjectiveDescription { get; private set; }
public DateTime StartDate { get; private set; }
public DateTime TargetDate { get; private set; }
public DateTime? CompletedDate { get; private set; }
public int EmploymentId { get; private set; }
public List<Skill> RelatedSkills { get; private set; }
public List<Goal> RelatedGoals { get; private set; }
// few more properties.
}
我有 2 个视图,一个是列表视图,另一个是详细信息视图。
ListView 有一个 IEnumerable,它只有 3 个字段
class ObjectiveListVM{
public int ObjectiveId { get; private set; }
public string ObjectiveDescription { get; private set; }
public DateTime StartDate { get; private set; }
}
“详细信息”视图有一个 ObjectiveDetailViewModel,其中包含 Objective 域模型中 90% 的字段以及更多字段。
我有一个存储库,可以为我提供一个列表或一个目标
IObjectiveRepo
{
Objective GetById();
IEnumerable<Objective> GetList();
}
这就是我实现 DDD 和存储库模式的方式。 我的问题是,我的 GetList 查询非常昂贵,它只需要 3 列的数据,但由于我的存储库应该始终返回域对象,所以我最终返回整个 Objective 域对象的列表,其中包含子列表和许多字段。
我想到的解决方案是拥有另一个 ObjectiveSummary 域模型,它只有几个字段,并由 GetList 存储库方法返回。但它随后打破了 DDD 的一些其他原则,主要是 ObjectiveSummary 是一个贫血领域模型。这不是一个模型,它更像是我脑海中的 DTO。
这是一个非常常见的场景,我觉得我在 DDD/存储库模式的实现或解释中遗漏了一些非常基本的东西。
一些专家能否指出我在实施中所犯的错误,或者强调一种无需昂贵查询即可解决此问题的方法?
注意:我可以想出几种解决这个问题的方法。然而,我更感兴趣的是找到不违反我正在使用的架构/模式原则的正确方法。
您不应该查询您的域模型。聚合总是完整加载,因此它不适合查询。
一旦您考虑延迟加载,您可能没有使用最佳方法。延迟加载是邪恶的。不要这样做:)
您所追求的是某种“查询层”。这与CQRS直接相关。查询端仅返回数据。它没有任何行为,您可以返回最基本的结构。在我所在的 C# 世界中,我使用 DataRow
或
IEnumerable<DataRow>
。如果它真的很复杂,我可能会选择 DTO:public interface IObjectiveQuery
{
DataRow ForList(int id);
bool Contains(string someUniqueKey);
IEnumerable<DataRow> Search(string descriptionLike, DateTime startDate);
string Description(int id);
}
尝试一下。我想你会发现它极大地简化了事情。您的域应该只关心事物的command
/write 方面。
IQueryable<Objective>
而不是
IEnumerable<Objective>
。public interface IObjectiveRepository
{
Objective GetById();
IQueryable<Objective> GetList();
}
它将允许您保持存储库简单,并向应用程序/域层中的查询添加更多逻辑,而不会损失性能。以下查询将在数据库服务器上执行,包括到
ObjectiveListVM
的投影:
public IReadOnlyList<ObjectiveListVM> GetSummaryList()
{
return _repository
.GetList()
.Select(o => new ObjectiveListVM
{
ObjectiveId = o.ObjectiveId,
ObjectiveDescription = o.ObjectiveDescription,
StartDate = o.StartDate
})
.ToList();
}
您可以使用 Automapper 的可查询扩展
来更轻松地投影到虚拟机。
return _repository
.GetList()
.ProjectTo<ObjectiveListVM>()
.ToList();