当存在单独的数据模型时,如何从存储库返回域对象?

时间:2018-02-10 09:23:52

标签: c# design-patterns domain-driven-design

更新

我的研究告诉我,我应该使用数据映射器:https://martinfowler.com/eaaCatalog/dataMapper.html。数据映射器是否注入了这样的存储库:http://www.rantdriven.com/post/2009/09/01/Using-the-Repository-Pattern-with-the-Command-and-DataMapper-Patterns.aspx和这个:Repository and Data Mapper pattern或者它们是否用作存储库的替代?我发现的所有示例似乎都使用Data Mappers将DataReader对象映射到域对象列表。我想将Persistent对象映射到Domain对象。

原始问题

在尝试这样的文章后,我正在尝试构建一个与数据模型完全隔离的域模型: http://blog.sapiensworks.com/post/2012/04/07/Just-Stop-It!-The-Domain-Model-Is-Not-The-Persistence-Model.aspx。目前我们只有六个人正在研究这个系统,但是未来可能会增加到9+。但是,我开始认为这不是正确的方法。我在这里阅读了很多问题,这些问题似乎告诉我将ORM直接映射到域模型。 我最近问过这个问题:https://softwareengineering.stackexchange.com/questions/365643/should-the-data-model-be-identical-to-the-domain-model-for-mapping-purposes。其中一位回答者说:"我认为两者之间的映射应该在(持久性导向的)存储库中#34;。你是如何做这个映射的?我不相信我应该在存储库中使用AutoMapper,原因如下所示:Repository pattern and mapping between domain models and Entity Framework和此处:http://enterprisecraftsmanship.com/2016/02/08/specification-pattern-c-implementation/即我不能简单地在存储库中执行此操作:

public Customer getId()
{
   CustomerData customerData = customerRepository.getById(id);
   return Mapper.Map<CustomerDomain>(customerData);
}

我不能这样做,因为不会考虑域对象的不变量。如何从存储库返回域对象。我是否将工厂注入存储库,该工具库将从数据模型中获取参数并返回域模型?这是否是正确的方法还是存在另一种将数据对象映射到域对象的模式?

2 个答案:

答案 0 :(得分:2)

快速审查存储库,由Evans定义

  

REPOSITORIES(提供)查找和检索持久对象的方法,同时封装所涉及的巨大基础设施。

     

(REPOSITORIES)提供内存收集的幻觉......

因此,API接口通常应该在特定于域的词汇表中表达;应用层和域模型都不需要知道API之外的任何细节。

通常,巨大的基础架构意味着与域无关的持久性设备;我们不坚持域对象,我们坚持字节。对于某些设备,我们与字节表示密切配合 - 将数据视为文件的流数据。在其他情况下,设备提供了这些字节的抽象--RDBMS为我们提供了一个理解表中行的API,并封装了字节排列方式的细节。

这意味着某处我们需要从域不可知表示转换为特定于域的表示,反之亦然。

通常,这些都采用函数的形式。

toDomainModel: JsonDocument -> DomainModel
toJson: DomainModel -> JsonDocument

这些功能通常由存储库实现定义,并由存储库实现调用 - 它们是Evans描述的巨大基础结构的一部分。

  

我不能这样做,因为不会考虑域对象的不变量。

这里有几种可能性。

当然,其中之一就是获得更智能的映射器。

第二种可能性是将模型的未经验证的表示形式建模为与经过验证的表示形式不同的东西。

示例:考虑Money的模型,需要AmountCurrencyCode;并且模型的语义要求Amount为正数,CurrencyCode为固定的标记集合中的条目。拥有UnvalidatedMoney类型,没有语义限制,以及将UnvalidatedMoney转换为Money(强制执行不变量)的函数没有错。

这与Scott Wlaschin描述的用于建模经过验证的电子邮件地址的内容类似。

请注意,这不一定是一种不适当的负担;如果你对某些域概念(例如money)有一个不变量,那么在将新输入从世界传递到模型时,你可能已经进行了验证。因此,工作主要是使验证元素可重复使用。

第三种可能性是研究Builder模式。从一个角度来看,持久对象是模型在过去编写的消息,以便将来可以读取它。因此,通常情况下,查看常见的消息传递模式很有用。

在这种方法中,我们将加载构建器的实例(由工厂为我们创建,由域模型实现),该实例具有允许存储库将域不可知数据传递给域模型的API。同样,由域模型提供的消息构建器知道域不变,并且可以对其进行验证。

  

域对象的所有验证都在构造函数中完成。我应该将工厂注入存储库吗?

那应该没问题。您希望将耦合保持为您可以管理的小(想想界面),并且您想要考虑工厂现在是该模型的公共API的一部分这一事实(想想如何更改工厂以使其保持向后兼容)。

答案 1 :(得分:2)

ORM和数据映射器

大多数.NET ORM框架在内部使用数据映射器,或者可以将其视为一种数据映射器。其他技术堆栈中的一些ORM可能使用替代方法进行映射:Active Record。两者之间的区别在于,在Active Record中,业务类知道如何持久化,而Data Mapper使其与任何持久性机制无关。

这与具有数据模型+域模型和直接映射到域模型之间的区别不同。无论您是直接映射到域还是拥有中间数据模型,您都将拥有一个Data Mapper。这两种方法都意味着它。 Data Mapper可以是ORM工具或自定义代码。

如果您打算在顶部同时使用ORM和其他数据映射器,那么您的问题并不清楚,但我不建议您尝试使用它或调用您的数据模型&lt; =&gt;域模型映射逻辑a&#34;数据映射器&#34;在PoEAA意义上的术语。

对象保湿和不变量

通常,数据映射器的域对象水合通过另一个路径而不是正常的用例驱动代码,以绕过所有可能发生的验证。这可以通过ORM或自定义代码可以访问的具有受限范围(protectedinternalinternalsVisibleTo)的无参数构造函数来完成。一些较新的基于反射的框架也可以访问私有字段。

除了您的域模型之外,使用数据模型,这一切都更简单,因为您不需要像Domain实体那样限制对数据模型对象的访问。数据模型不具有不变量,您可以安全地保持其无参数构造函数和属性可公开访问并可设置以进行补液。您唯一需要注意的是在您的域实体上有一些FromData()方法或构造函数,它将数据模型作为输入并实例化实体。同样,由于可访问性级别限制,该技术变得安全 - 水合代码通常可以在域授予internalsVisibleTo的程序集中。