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

C# DDD:映射到具有大量数据库列的表的实体,c#,domain-driven-design,entities,C#,Domain Driven Design,Entities,我试图遵循DDD方法来构造实体,其中属性具有私有setter和公共getter,并且赋值是通过构造函数完成的。这种方法的问题似乎是,当你点击一个有很多列的表时,我们有一个表至少有40列。这很快就变成了一场噩梦。我发现一些文章似乎指向流畅的界面或工厂模式。看起来像是40列,尽管cleaner仍然可能失去控制,即使有这些模式。这些列与表设计相关,不违反SRP。此外,在试图保持表的大小,并可能根据逻辑分组将实体拆分为更小的值对象时,似乎仍有一些值对象将变大。有人能告诉我如何在不破坏DDD的情况下处理这

我试图遵循DDD方法来构造实体,其中属性具有私有setter和公共getter,并且赋值是通过构造函数完成的。这种方法的问题似乎是,当你点击一个有很多列的表时,我们有一个表至少有40列。这很快就变成了一场噩梦。我发现一些文章似乎指向流畅的界面或工厂模式。看起来像是40列,尽管cleaner仍然可能失去控制,即使有这些模式。这些列与表设计相关,不违反SRP。此外,在试图保持表的大小,并可能根据逻辑分组将实体拆分为更小的值对象时,似乎仍有一些值对象将变大。有人能告诉我如何在不破坏DDD的情况下处理这种情况的正确方向吗?

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

当然,秘诀是不要让任何消费代码只为域对象分配任何值,而不遵循正确的“域逻辑”,从而隐藏setter。 当然,有时您必须将所有这些值设置为域对象,并且这些值来自某些dto或mvc模型或其他东西,然后使用可以映射/分配这些值的映射器类是保持代码整洁的好方法

您甚至可以考虑使用Jimmy Bogard的AutoMapper:顺便说一句,它也可以将值分配给私有setter

如果使用ORM加载数据,其中一些支持私有setter和/或backing字段。 例如,NHibernate允许在映射文件中执行以下操作:

this.Property(x => x.Description, mapper => mapper.Access(Accessor.Field))
根据我的评论,使用AutoMapper之类的工具来减轻大量映射代码的编写。 将实现封装在一个可注入类中,如下所示,这样您就可以在将来替换实现,而无需执行鸟枪手术

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();
    }
}
公共类视图映射器:IViewMapper
{
公共TDomain映射域(TModel数据项)
{
返回Mapper.Map(数据项);
}
公共列表映射域(IEnumerable数据项)
{
返回dataItems.Select(this.MapToDomain.ToList();
}
公共TModel MapToData(tDomainItem)
{
返回Mapper.Map(domainItem);
}
public void MapToOriginalData(tDomainItem、TModel dataItem)
{
Map(domainItem,dataItem);
}
公共列表映射数据(IEnumerable domainItems)
{
返回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());
    }
}
public类ViewItemProfile:AutoMapper.Profile
{
受保护的覆盖无效配置()
{
Mapper.CreateMap()
.ForMember(x=>x.ErrorRequestId,y=>y.MapFrom(z=>z.ErrorTypeId))
.ForMember(x=>x.不相关,y=>y.Ignore());
}
}

DDD很难(有时可能不可能)改进到设计糟糕、以数据库为中心的系统。您是说DDD不适合于具有大列的表吗?这种逻辑似乎有点局限性。从数据库的角度来看,一个包含大量列的表似乎并不那么不现实。您将为特定领域建模,将领域知识封装在实体、价值对象和服务中。我担心在现有系统上建立新的DDD系统的模型,该系统有40个列表,这样做是错误的。我无法想象如果我有一个传统的以数据为中心的系统,有一个40列的
Customer
表,那么这些列中的25到30列怎么可能不存在于我的
Customer
实体中。在我新的
客户
实体中加入40个col将是一个基于数据库向上构建的设计。我非常同意Adrian的评论。您应该真正考虑域的设计,而不是数据库。如何保存数据并将其转换为现有的数据库表应该在以后完成。如果您可以选择(没有其他系统使用该数据库)迁移和更改数据库,这也会有所帮助。这是一个很好的观点。大部分信息来自用户通过ViewModel输入的信息。在这种情况下,我应该在构造函数参数列表中包含数据库中的所有字段,还是应该选择参数列表中的内容?似乎并不能真正代表数据库。是的,你不应该试图模仿数据库。DDD的重点是对业务解决方案进行建模,以及如何将其持久化到DB/File/MemoryStream或任何外部关注点。构造函数需要使对象构造处于有效状态的东西;不一定只是它携带的所有数据。我要说的是,在构造函数中传递的内容要更具选择性。如果您的大多数案例都是通过ViewModel进行的,请使用映射器,甚至尝试使用一个可以使事情变得更简单的框架。我将在我的答案中添加一个映射器。很好。到目前为止,我使用mapper的方式很糟糕。这是一个非常干净的选择。谢谢,这真的很干净。我有一些重构要做:-)。只是想了想。如果要使用这种方法,我是否需要为AutoMapper添加一个零参数构造函数?是的,AutoMapper确实需要一个默认构造函数-这又是一个证明,你有时需要在这里或那里付出一些。你也会注意到有时候你的ORM会泄露一些它的实现