Pregunta

Besides translating Relational data to Object model, The ORM has other roles, as:

  1. Lazy Loading
  2. Automatic change detection
  3. Transactions

However, with Repository pattern translating ORM's DTOs to Domain Models, that happens:

  1. Can't use the benefits of Lazy Load, since I need to fill my entire Domain Model and Repository doesn't know what data Domain needs.
  2. ORM can't detect changes, since Domain Model isn't from ORM world.
  3. Can't do many transactions at once, again, due the lack of Domain knowledge about ORM

Question 1: Am I missing some gap where I can have the full benefits of Lazy Loading, Transactions and Automatic change detection in a scenario? Or these benefits are more for another approach (such as Active Record) than DDD?

Question 2: Why is so mentioned in DDD books? Just for the relational to domain model and Lazy Loading, Transactions and Change Detection is fully discarted

Some platforms has code-first approach, which is a way to improve these problems, however this feature is not always present in many environments or simply is not able to use (in a legacy database for eg.), so it's not a solution.

¿Fue útil?

Solución

I have been thinking for some time that if one were to remove the change-tracking from an ORM then developers would see far less value in them.

Lazy-loading should never happen. An aggregate is always loaded in its entirety. If you find yourself requiring lazy-loading chances are that you are querying your domain model. This is something you should not do. Use a simple query layer / read model for that.

Transactions really are a DB concern and would not impact DDD directly. Aggregates do represent a consistency boundary so a database transaction is a natural fit but that is where it ends.

You could still use an ORM tool with DDD but you probably will get less mileage. I do not like ORMs at all and if I have any choice in the matter I simply do not use them. The mapping is really not all that much work and if done in a custom mapper class runs at language speeds as opposed to some proxy mechanism.

There are instances that I have seen where domain objects are, for instance, persisted directly using an ORM. However, if I have to mark anything using, say, an attribute or even change my design where I have to implement certain methods as virtual or even structure certain classes in a specific way I no longer regard my domain as persistent ignorant, and that is something I really want (PI).

Otros consejos

However, with Repository pattern translating ORM's DTOs to Domain Models, that happens:

  1. Can't use the benefits of Lazy Load, since I need to fill my entire Domain Model and Repository doesn't know what data Domain needs.
  2. ORM can't detect changes, since Domain Model isn't from ORM world.
  3. Can't do many transactions at once, again, due the lack of Domain knowledge about ORM

There is no need to implement another layer for mapping ORM's entities into Domain Entities when modern ORMs are used. In the .NET world either Entity Framework or NHiberate are able to map your Rich Domain Model into Relational Data Base.

Code-First approach can be used with both of them.

First Domain Model is designed, then Data Base is generated using mappings (Entity Framework Fluent Api / Fluent NHibernate Mappings) or conventions (EF Custom Code First Conventions / Fluent NHibernate Auto Mapping)

There are some related SO questions:

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

Why ORM is so mentioned in DDD books? Just for the relational to domain model and Lazy Loading, Transactions and Change Detection is fully discarted

You can find DDD implementation for the Cargo system described in the blue book in http://dddsample.sourceforge.net/. It uses Hibernate as the ORM.

There are database transactions in application services, for example see se.citerus.dddsample.application.impl.BookingServiceImpl. @Transactional is annotation from Spring that causes method to be wrapped in a database transaction.

Change detection is not discarded. Repository in the original DDD pattern doesn't have update method, so change detection (ORM) is used to update domain objects. For example:

@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");
}

Actually in the sample, repository has update method, because cargoRepository.store() is the update method:

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();
}

Surprisingly, you can find the usage of lazy collection in the official sample, for example in 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>

So, the answer is you can still have all the benefit of your ORM.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top