我希望能够通过新建视图模型并将贡献域模型作为参数传递(如下面的代码),将域模型映射到视图模型。我的动机是不再重复使用映射代码和提供一种简单的映射方式(不使用自动化器)。一位朋友说,视图模型不应该知道有关传递给可选构造函数的“支付”域模型的任何信息。你觉得怎么样?
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; }
}
答案 0 :(得分:5)
你的朋友是对的。观点应该是愚蠢的,对你的领域模型一无所知。
您是否尝试使用Automapper将您的商家/域实体/模型映射到您的dto / viewmodels?
更多细节因为评论:
将映射代码放在viewmodel中会违反关注点,SOLID单一责任主体,MVC模式和域驱动设计主体。视图有一个责任,将数据提供给屏幕,就是这样。恕我直言,没有多少争论。它只是一个坏主意,违反了许多核心软件开发原则。
答案 1 :(得分:4)
这是一个相当古老的问题,已经接受了答案,但是为了详细说明所提供的答案并解决评论部分中提出的问题,正在提供这个额外的答案。
正如所提出的那样,这个问题有点无限期。当被问及是否应该以某种方式对软件开发进行某些操作时,可以将该问题理解为关于管理相关主题的基本设计原则的问题,何时应该应用这样的设计原则,或者两者都适用。为了帮助客观地讨论这个话题,让我们依次考虑这两个方面。
创建构造函数以将值从一个对象映射到另一个对象的特定做法会在两个对象之间创建耦合。视图模型包含与系统内特定视图相关的属性和/或行为。由于此类对象的目的是为特定视图建模,因此封装用于从系统内的另一种类型初始化模型的内部状态/值的映射逻辑意味着视图模型现在包含可能需要修改的代码除了更改视图的建模方式之外。这些变化打开了模型行为的其他方面可能受到不利影响的可能性,从而导致系统行为的意外回归。通过耦合多个关注点来控制系统内部组件分离以防止意外的行为回归的原则被称为单一责任原则。
应该何时应用这些原则的问题有点困难。重要的是要记住,软件通常是以一些目标为基础编写的(例如解决一些业务问题,促进娱乐或教育等),对于任何给定的软件而言,最好的是与任务相关的在眼前。对于作为特定公司的旗舰产品而被创建的软件系统所做的选择可能与为解决直接问题而开发的系统所做的选择完全不同。还需要考虑为促进某些类型的解耦所需的工作范围。一些解耦技术相对容易合并,而其他解耦技术可能更难,甚至可以为初始实现以及添加到负责软件维护的团队的每个新开发人员提供步骤学习曲线。虽然没有启发式方法可以做出这样的决定,但是测试驱动开发提出了在重复存在之前不引入抽象的启发式方法。例如,策略模式是一种优秀的技术,用于遵循开放/封闭原则,这是一种管理对象设计的原则,允许在不同场景中应用它们,而无需修改现有代码。但是,在遵循测试驱动开发实践时,在观察到第二个用例之前,不会引入策略模式。通过遵循这种启发式方法,开发人员不得不通过编写完成任务所必需的代码而不重复来限制他们对手头任务的努力,从而最大限度地减少浪费并最大限度地提高可维护性(通过最小化复杂性)。 p>
然而,软件工程既是科学又是艺术。它是一门科学,因为有一些规则可以控制能够和不能达到某些目的,但它也是一门艺术,因为你做得越多就会越好。必须做出明确的权衡取舍,最终必须在主观上做出。例如,作为客户端软件开发人员,我通常从不参与具有短寿命的应用程序的设计和开发。因此,在将基于约定的依赖注入引入我的应用程序之前,我不会等到看到重复。在应用程序中引入依赖注入的一致性使用在软件系统开始时的成本远远低于等待直到您开始觉得需要它的成本。
关于在视图模型中添加映射代码的具体示例,虽然它确实将视图模型耦合到特定的域模型,但实际上我不会发现这是一个很大的问题。视图模型不可能与其他域模型一起使用,并且引入的代码类型(即映射)的性质通常不包含业务逻辑,因此此SRP违规导致显着回归的可能性在系统中远远小于应用程序或域层的SRP违规。
那就是说,我没有找到在构造函数中添加映射逻辑的过程,可以节省大量的时间。如果要在大多数语言中创建一个单独的类来封装域对象和视图模型之间的映射,我们只讨论额外的几行代码。以下是实施方面的差异:
// constructor
public ViewType(DomainType domainType) {
...
}
// mapper class
public class ViewTypeMapper {
public ViewType Map(DomainType domainType) {
...
}
}
因此,您要么返回新的ViewType(domainType),要么返回新的ViewTypeMapper()。Map(domainType)。我只是不知道在这种情况下脱钩会增加任何重要的工作。在大多数情况下,你甚至已经浪费了你的公司或客户的时间和金钱,甚至讨论过它,因为你总是最终谈论它的时间更长。时间比你只是创建单独的类来表示映射,或者你是否要继续设置Automapper。