除了将关系数据转换为对象模型外, ORM 还有其他角色,如:
- 延迟加载
- 自动更改检测
- 交易
醇>
但是,通过存储库模式将 ORM 的DTO转换为域模型,会发生这种情况:
- 无法使用Lazy Load的好处,因为我需要填充整个域模型,存储库不知道域需要什么数据。
- ORM 无法检测到更改,因为域模型不是来自 ORM 世界。
- 由于缺乏域 ORM
的知识,再次无法做多次交易 醇>
问题1:我是否遗漏了一些差距,我可以在domain-driven-design方案中获得延迟加载,交易和自动更改检测的全部好处?或者这些好处更多的是针对另一种方法(例如Active Record)而不是DDD?
问题2:为什么DDD书籍中提到了orm?仅针对域模型的关系和延迟加载,交易和变更检测完全放弃
某些平台采用代码优先方法,这是一种改善这些问题的方法,但是这个功能在许多环境中并不总是存在,或者根本无法使用(例如,在遗留数据库中。),所以这不是解决方案。
答案 0 :(得分:19)
我一直在想,如果要从ORM中删除更改跟踪,那么开发人员会看到更少的价值。
永远不应该进行延迟加载。总是加载聚合。如果您发现自己需要延迟加载,则可能是您正在查询域模型。这是你不应该做的事情。为此使用简单的查询图层/读取模型。
交易确实是数据库关注的问题,不会直接影响DDD。聚合确实表示一致性边界,因此数据库事务很自然,但它就是它的结束位置。
您仍然可以使用带有DDD的ORM工具,但您可能会获得更少的里程数。我根本不喜欢ORM,如果我有任何选择,我根本就不使用它们。映射实际上并不是那么多工作,如果在自定义映射器类中完成,则以语言速度运行而不是某些代理机制。
有些实例我已经看到域对象在哪里,例如,使用ORM直接持久化。但是,如果我必须使用某个属性标记任何内容,甚至更改我的设计,我必须将某些方法实现为virtual
,或者甚至以特定方式构造某些类,我不再认为我的域名是持久的无知, 是我真正想要的东西(PI)。
答案 1 :(得分:4)
但是,使用Repository模式将ORM的DTO转换为Domain 发生的模型:
- 无法使用Lazy Load的好处,因为我需要填写我的整个域模型和存储库不知道什么数据域 需求。
- ORM无法检测到更改,因为域模型不是来自ORM世界。
- 由于缺乏关于ORM的领域知识,
- 无法再次进行多次交易
醇>
当使用现代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问题:
答案 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的所有好处。