DDD:映射到具有大量数据库列的表的实体

DDD: entities mapped to tables with lots of databse columns

我正在尝试按照 DDD 方法构建实体,其中属性具有私有 setter 和 public getter,并且赋值是通过构造函数完成的。这种方法的问题似乎是当你点击一个有很多列的 table 时,我们有一个至少有 40 列。它很快变成了一场噩梦。我发现一些文章似乎指向 Fluent Interface 或 Factory Pattern。看起来像 40 列,但即使使用这些模式,清洁器仍然可能失控。这些列与 table 设计相关,并且不违反 SRP。此外,在尝试保持 table 大小并可能根据逻辑分组将实体分解为更小的值对象时,看起来某些值对象仍然会很大。有人可以指出如何在不破坏 DDD 的情况下处理这种情况的正确方向吗?

并不是说 DDD 不适合宽列。 隐藏 setter 也不是 DDD 的特定要求,而是普通的旧封装。 (保护您的数据的修改。) 如果您必须设置很多这些值并且发现自己混淆了代码,那么可以像您提到的那样,将构造移至工厂。

秘诀当然是不要让任何消费代码在不遵循正确的 "Domain Logic" 的情况下将任何值分配给您的域对象,因此隐藏了 setter。 当然,有时您必须将所有这些值设置为您的域对象,并且这些值来自某些 dto 或 mvc 模型或其他东西,然后使用可以 map/assign 值的映射器 class 是一个很好的方法保持你的消费代码干净。

您甚至可以考虑使用 Jimmy Bogard 的 AutoMapper 之类的东西:http://automapper.org/ 顺便说一下,它也可以毫无问题地将值分配给私有 setters。

如果您使用 ORM 加载数据,其中一些支持私有 setters and/or 支持字段。 例如,NHibernate 在您的映射文件中允许这样做:

this.Property(x => x.Description, mapper => mapper.Access(Accessor.Field))

根据我的评论,使用类似 AutoMapper 的东西来减轻大量映射代码的编写。 将实现包裹在可注射的 class 中,如下所示,这将允许您在将来替换实现而无需执行猎枪手术。

public class ViewMapper<TModel, TDomain> : IViewMapper<TModel, TDomain>
{
    public TDomain MapToDomain(TModel dataItem)
    {
        return Mapper.Map<TModel, TDomain>(dataItem);
    }

    public List<TDomain> MapToDomain(IEnumerable<TModel> dataItems)
    {
        return dataItems.Select(this.MapToDomain).ToList();
    }

    public TModel MapToData(TDomain domainItem)
    {
        return Mapper.Map<TDomain, TModel>(domainItem);
    }

    public void MapToOriginalData(TDomain domainItem, TModel dataItem)
    {
        Mapper.Map(domainItem, dataItem);
    }

    public List<TModel> MapToData(IEnumerable<TDomain> domainItems)
    {
        return domainItems.Select(this.MapToData).ToList();
    }
}

AutoMapper 是高度可配置的,应该能够处理大多数情况。 设置一个映射器配置文件,您可以在其中准确地告诉它在映射期间要做什么:

public class ViewItemProfile : AutoMapper.Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<Domain, View>()
            .ForMember(x => x.ErrorRequestId, y => y.MapFrom(z => z.ErrorTypeId))
            .ForMember(x => x.Irrelevant, y => y.Ignore());
    }
}