我应该使用可选的构造函数将域对象映射到视图模型吗?

Byr*_*ahl 6 asp.net-mvc viewmodel

我希望能够通过新建视图模型并将贡献域模型作为参数传递(如下面的代码),将域模型映射到视图模型.我的动机是不再重复使用映射代码和提供一种简单的映射方式(不使用自动化器).一位朋友说,视图模型不应该知道有关传递给可选构造函数的"支付"域模型的任何信息.你怎么看?

public class LineItemsViewModel
{
    public LineItemsViewModel()
    {
    }

    public LineItemsViewModel(IPayment payment)
    {
        LineItemColumnHeaders = payment.MerchantContext.Profile.UiPreferences.LineItemColumnHeaders;
        LineItems = LineItemDomainToViewModelMapper.MapToViewModel(payment.LineItems);
        ConvenienceFeeAmount = payment.ConvenienceFee.Fee;
        SubTotal = payment.PaymentAmount;
        Total = payment.PaymentAmount + payment.ConvenienceFee.Fee;
    }

    public IEnumerable<Dictionary<int, string>> LineItems { get; set; }
    public Dictionary<int, string> LineItemColumnHeaders { get; set; }
    public decimal SubTotal { get; set; }
    public decimal ConvenienceFeeAmount { get; set; }
    public decimal Total { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ell 6

你的朋友是对的.视图应该是愚蠢的,并且对您的域模型一无所知.

您是否尝试使用Automapper将业务/域实体/模型映射到dto/viewmodels?

更多细节因评论:

将映射代码放在viewmodels中会违反关注点,SOLID单一责任主体,MVC模式和域驱动设计原则.视图有一个责任,将数据提供给屏幕,就是这样.恕我直言,没有多少争论.它只是一个坏主意,违反了许多核心软件开发原则.


Der*_*eer 6

这是一个比较老的问题,已经接受了答案,但是为了详细说明提供的答案并解决注释部分中提出的问题,正在提供此附加答案。

正如所提出的,这个问题有点不确定。当被问及是否应该以某种方式对软件开发进行某些处理时,该问题可以理解为一个与管辖所讨论主题的基础设计原则有关的问题,何时应统一采用这些设计原则,或者两者都应适用。 。为了帮助客观地讨论该主题,让我们依次考虑这两个方面。

创建构造函数以将值从一个对象映射到另一个对象的特定做法在两个对象之间建立了耦合。视图模型包含与系统内特定视图有关的属性和/或行为。由于此对象的目的是为特定视图建模,因此将映射逻辑封装为用于初始化系统中另一种类型的模型的内部状态/值的映射逻辑意味着该视图模型现在包含出于某些原因可能需要修改的代码除了更改视图建模方式之外。这样的变化可能会影响模型行为的其他方面,从而导致系统行为的意外回归。

何时应采用此类原则的问题更加困难。重要的是要记住,编写软件时通常会考虑到某些目标(例如,解决一些业务问题,促进娱乐或教育等),并且对于任何给定的软件而言,最好的选择是相对于手头的任务。对于要被创建为特定公司的旗舰产品的软件系统,所做的选择可能与针对解决紧急问题而开发的系统所做的选择完全不同。还需要考虑为促进某些类型的去耦而需要的工作范围。一些解耦技术相对容易集成,而另一些解耦技术则可能更困难,甚至具有针对初始实施以及加入到负责软件维护的团队中的每个新开发人员的逐步学习曲线。尽管没有启发式方法可以完美地做出此类决策,但测试驱动开发提出了一种启发式方法,即在存在重复之前不引入抽象。例如,策略模式是遵循开放/封闭原则的一项出色技术,该原则支配对象的设计,以允许对象在不同场景中的应用而无需修改现有代码。但是,当遵循“测试驱动开发”实践时,只有在观察到第二个用例之后,才会引入策略模式。

尽管如此,软件工程既是一门科学,也是一门艺术。这是一门科学,因为有规则来控制为达到特定目的而可以做什么和不可以做什么,但是,这也是一门艺术,您越做越好,就越做得更好,并且需要做出一定的取舍。最终必须主观地进行。例如,作为客户端软件开发人员,我通常从不参与使用寿命短的应用程序的设计和开发。因此,在将基于约定的依赖项注入到应用程序中之前,我不会等到看到重复。在软件系统生命周期的开始阶段,在应用程序中引入一致性注入的成本要比等到您开始感到需要它时要低得多。

关于在视图模型中添加映射代码的特定示例,尽管它确实将视图模型耦合到特定的域模型,但实际上,我认为这并不是一个大问题。视图模型不太可能与其他域模型一起使用,并且引入的代码类型(即映射)的性质通常不包含业务逻辑,因此这种SRP违规导致系统严重退化的可能性为远远少于应用程序或域层的SRP违规情况。

就是说,我发现在构造函数中添加映射逻辑的过程不会节省任何时间。如果要创建一个单独的类来封装大多数语言中的域对象和视图模型之间的映射,那么我们只是在谈论额外的几行代码。这是实现上的区别:

// constructor
public ViewType(DomainType domainType) {
...
}


// mapper class
public class ViewTypeMapper { 
  public ViewType Map(DomainType domainType) {
  ...
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,您正在执行返回新的ViewType(domainType),或者正在执行返回的新ViewTypeMapper()。Map(domainType)。我只是看不到这种情况下的去耦会增加任何重要的工作。在大多数情况下,您甚至甚至都没有讨论过,因此已经浪费了公司或客户的时间和金钱,因为与只创建单独的类来表示相比,您无休止地谈论它会花费更长的时间。映射,或者直接设置Automapper。