data - ¿El patrón de repositorio mata a ORM?
spring-orm maven (3)
¿Por qué ORM es tan mencionado en los libros de DDD? Solo para el modelo relacional al dominio y la carga diferida, las transacciones y la detección de cambios se descartan por completo
Puede encontrar la implementación de DDD para el sistema Cargo descrito en el libro azul en http://dddsample.sourceforge.net/ . Utiliza Hibernate como el ORM.
Hay transacciones de base de datos en servicios de aplicaciones, por ejemplo, ver se.citerus.dddsample.application.impl.BookingServiceImpl
. @Transactional
es una anotación de Spring que hace que el método se ajuste en una transacción de base de datos.
La detección de cambios no se descarta. El repositorio en el patrón DDD original no tiene un método de actualización, por lo que la detección de cambios (ORM) se usa para actualizar los objetos del dominio. Por ejemplo :
@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");
}
En realidad, en el ejemplo, el repositorio tiene un método de actualización, porque cargoRepository.store()
es el método de actualización:
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();
}
Sorprendentemente, puede encontrar el uso de la recolección perezosa en la muestra oficial, por ejemplo en 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>
Entonces, la respuesta es que todavía puede tener todos los beneficios de su ORM.
Además de traducir los datos relacionales al modelo de objetos, el ORM tiene otros roles, como:
- Carga lenta
- Detección automática de cambios
- Actas
Sin embargo, con el patrón de Repository traduciendo las DTO de ORM a los modelos de dominio , eso sucede:
- No puedo usar los beneficios de Lazy Load, ya que necesito llenar todo el Modelo de Dominio y el Repositorio no sabe qué datos necesita el Dominio.
- ORM no puede detectar cambios, ya que el modelo de dominio no es del mundo ORM .
- No se pueden hacer muchas transacciones a la vez, nuevamente, debido a la falta de conocimiento del Dominio sobre ORM
Pregunta 1: ¿Me estoy perdiendo una brecha en la que pueda tener todos los beneficios de la carga diferida, las transacciones y la detección automática de cambios en un escenario de domain-driven-design ? ¿O estos beneficios son más para otro enfoque (como Active Record) que DDD?
Pregunta 2: ¿Por qué orm está tan mencionado en los libros de DDD? Solo para el modelo relacional al dominio y la carga diferida, las transacciones y la detección de cambios se descartan por completo
Algunas plataformas tienen un enfoque de código primero, que es una forma de mejorar estos problemas, sin embargo, esta característica no siempre está presente en muchos entornos o simplemente no puede usarse ( en una base de datos heredada, por ejemplo ), por lo que no es una solución.
Sin embargo, con el patrón de Repository traduciendo los DTO de ORM a los modelos de dominio, eso sucede:
- No puedo usar los beneficios de Lazy Load, ya que necesito llenar todo el Modelo de Dominio y el Repositorio no sabe qué datos necesita el Dominio.
- ORM no puede detectar cambios, ya que el modelo de dominio no es del mundo ORM.
- No se pueden hacer muchas transacciones a la vez, nuevamente, debido a la falta de conocimiento del Dominio sobre ORM
No es necesario implementar otra capa para asignar entidades de ORM a entidades de dominio cuando se utilizan ORM modernos. En el mundo .NET, Entity Framework o NHiberate pueden asignar su modelo de dominio enriquecido a la base de datos relacionales.
El enfoque Code-First se puede utilizar con ambos.
El primer modelo de dominio está diseñado, luego la base de datos se genera mediante asignaciones ( Entity Framework Fluent Api / Fluent NHibernate Mappings ) o convenciones ( EF Custom Code First Conventions / Fluent NHibernate Auto Mapping )
Hay algunas preguntas de SO relacionadas:
He estado pensando durante algún tiempo que si uno eliminara el seguimiento de cambios de un ORM, los desarrolladores verían mucho menos valor en ellos.
La carga perezosa nunca debería suceder. Un agregado siempre se carga en su totalidad. Si necesita realizar una carga lenta, es posible que esté consultando su modelo de dominio. Esto es algo que no debes hacer. Utilice un modelo de lectura / capa de consulta simple para eso.
Las transacciones realmente son una preocupación de base de datos y no afectarían directamente a DDD. Los agregados representan un límite de coherencia, por lo que una transacción de base de datos es un ajuste natural, pero ahí es donde termina.
Aún puede usar una herramienta ORM con DDD pero probablemente obtendrá menos kilometraje. No me gustan los ORM en absoluto y, si tengo alguna opción, simplemente no los uso. La asignación no es realmente tanto trabajo y, si se realiza en una clase de asignador personalizado, se ejecuta a velocidades de idioma en lugar de algún mecanismo proxy.
Hay instancias que he visto donde los objetos de dominio, por ejemplo, persisten directamente usando un ORM. Sin embargo, si tengo que marcar algo usando, digamos, un atributo o incluso cambiar mi diseño donde tengo que implementar ciertos métodos como virtual
o incluso estructurar ciertas clases de una manera específica, ya no considero que mi dominio es un ignorante persistente, y eso es algo que realmente quiero (PI).