存储库模式是否会导致ORM死亡?

时间:2014-04-21 22:47:40

标签: orm domain-driven-design repository-pattern

除了将关系数据转换为对象模型外, ORM 还有其他角色,如:

  
      
  1. 延迟加载
  2.   
  3. 自动更改检测
  4.   
  5. 交易
  6.   

但是,通过存储库模式将 ORM 的DTO转换为域模型,会发生这种情况:

  
      
  1. 无法使用Lazy Load的好处,因为我需要填充整个域模型存储库不知道域需要什么数据。
  2.   
  3. ORM 无法检测到更改,因为域模型不是来自 ORM 世界。
  4.   
  5. 由于缺乏 ORM
  6. 的知识,再次无法做多次交易   

问题1:我是否遗漏了一些差距,我可以在方案中获得延迟加载,交易和自动更改检测的全部好处?或者这些好处更多的是针对另一种方法(例如Active Record)而不是DDD?

问题2:为什么DDD书籍中提到了?仅针对域模型的关系和延迟加载,交易和变更检测完全放弃

某些平台采用代码优先方法,这是一种改善这些问题的方法,但是这个功能在许多环境中并不总是存在,或者根本无法使用(例如,在遗留数据库中。),所以这不是解决方案。

3 个答案:

答案 0 :(得分:19)

我一直在想,如果要从ORM中删除更改跟踪,那么开发人员会看到更少的价值。

永远不应该进行延迟加载。总是加载聚合。如果您发现自己需要延迟加载,则可能是您正在查询域模型。这是你不应该做的事情。为此使用简单的查询图层/读取模型。

交易确实是数据库关注的问题,不会直接影响DDD。聚合确实表示一致性边界,因此数据库事务很自然,但它就是它的结束位置。

您仍然可以使用带有DDD的ORM工具,但您可能会获得更少的里程数。我根本不喜欢ORM,如果我有任何选择,我根本就不使用它们。映射实际上并不是那么多工作,如果在自定义映射器类中完成,则以语言速度运行而不是某些代理机制。

有些实例我已经看到域对象在哪里,例如,使用ORM直接持久化。但是,如果我必须使用某个属性标记任何内容,甚至更改我的设计,我必须将某些方法实现为virtual,或者甚至以特定方式构造某些类,我不再认为我的域名是持久的无知, 是我真正想要的东西(PI)。

答案 1 :(得分:4)

  

但是,使用Repository模式将ORM的DTO转换为Domain   发生的模型:

     
      
  1. 无法使用Lazy Load的好处,因为我需要填写我的整个域模型和存储库不知道什么数据域   需求。
  2.   
  3. ORM无法检测到更改,因为域模型不是来自ORM世界。
  4.   
  5. 由于缺乏关于ORM的领域知识,
  6. 无法再次进行多次交易
  7.   

当使用现代ORM时,无需实施另一层将ORM实体映射到域实体。在.NET世界中,Entity Framework或NHiberate都能够将Rich Domain Model映射到Relational Data Base。

Code-First方法可以与它们一起使用。

设计了第一个域模型,然后使用映射(Entity Framework Fluent Api / Fluent NHibernate Mappings)或约定(EF Custom Code First Conventions / Fluent NHibernate Auto Mapping生成数据库

有一些相关的SO问题:

  1. Advice on mapping of entities to domain objects
  2. Repository pattern and mapping between domain models and Entity Framework

答案 2 :(得分:0)

  

为什么在DDD书籍中提到ORM?只是为了关系   域模型和延迟加载,事务和变更检测是   完全放弃

您可以在http://dddsample.sourceforge.net/的蓝皮书中找到货物系统的DDD实施。它使用Hibernate作为ORM。

应用程序服务中有数据库事务,例如,请参阅se.citerus.dddsample.application.impl.BookingServiceImpl@Transactional是Spring的注释,它使方法被包装在数据库事务中。

不会丢弃更改检测。 原始DDD模式中的存储库没有更新方法,因此更改检测(ORM)用于更新域对象。例如

@Override
@Transactional
public void assignCargoToRoute(final Itinerary itinerary, final TrackingId trackingId) {
   final Cargo cargo = cargoRepository.find(trackingId);
   if (cargo == null) {
      throw new IllegalArgumentException("Can't assign itinerary to non-existing cargo " + trackingId);
   }

   cargo.assignToRoute(itinerary);
   cargoRepository.store(cargo);

   logger.info("Assigned cargo " + trackingId + " to new route");
}

实际上,在示例中,存储库具有更新方法,因为cargoRepository.store()是更新方法:

public void store(Cargo cargo) {
   getSession().saveOrUpdate(cargo);
   // Delete-orphan does not seem to work correctly when the parent is a component
   getSession().createSQLQuery("delete from Leg where cargo_id = null").executeUpdate();
}

令人惊讶的是,您可以在官方示例中找到lazy collection的用法,例如 src / main / resources / se / citerus / dddsample / infrastructure / persistence / hibernate / Cargo.hbm.xml

<hibernate-mapping default-access="field">
   <class name="se.citerus.dddsample.domain.model.cargo.Cargo" table="Cargo">

   ...

   <component name="itinerary">
       <list name="legs" lazy="true" cascade="all">
           <key column="cargo_id" foreign-key="itinerary_fk"/>
           <index column="leg_index"/>
           <one-to-many class="se.citerus.dddsample.domain.model.cargo.Leg"/>
       </list>
   </component>
   </class>
</hibernate-mapping>

所以,答案是你仍然可以获得ORM的所有好处。