viernes, 7 de diciembre de 2018

Los agregados no son elementos persistibles.

Si habéis leído ciertas entradas en este blog; alguna vez me habréis visto comentar que hacer que un agregado sea un elemento que se guarda en persistencia a bloque (y por ende se lee de persistencia a bloque) es un detalle de implementación que no es obligatorio y no debe afectar al diseño del agregado ni que este dependa del esquema de persistencia.


Pues bien: Ni pu** caso, ni detalle de implementación ni pitufos en vinagre. Directamente NO. No es un elemento persistible. Un agregado no es un puñado de "tablas de BD" representadas en memoria, y un agregado raíz no es añadir funciones públicas a una de esas "tablas". De hecho, un agregado raíz no tiene ni porqué tener identificador único.

Un agregado es un elemento que se encarga de comprobar las invariantes del dominio y aplicar las reglas de los cambios de ese mismo dominio.

Comprobar las invariantes del dominio significa: "¿Puedo hacer esta operación según el estado actual del sistema?" y para ello leemos de persistencia la información justa y necesaria para ello. Cogemos un campo de una tabla, dos campos de otra tabla, el número de entradas de una tercera, etc; con ello montamos el agregado y este se encarga comprobar que la operación es posible.

Aplicar las reglas de los cambios del dominio significa: "Ahora que ya sé que la operación es posible voy a realizar los cálculos necesarios para obtener el nuevo estado del sistema una vez realizada la operación". A veces es necesaria alguna información del sistema para realizar estos cálculos por lo que la información justa y necesaria para montar el agregado (lo expuesto en el punto anterior) se amplía para que abarque también estos datos.

Ojo, que no estoy diciendo que para montar el agregado no se usen VO's y entidades. Claro que se usan. Esos datos dispersos de persistencia que se necesita leer se usan para crear VO's y entidades para alimentar el agregado y que trabaje con ellos.

Con elemento persistible me refiero a algo como esto:

public class ApplicationServices.MyService{

  public void DoSomething(...){
     MyAggregate agg = myAggRepository.Get(aggId);
     agg.DoSomething(...); //cambia su estado interno con el nuevo estado del sistema
     myAggRepository.Save(agg); //coge su estado interno y lo persiste
  }

}

Al hacer esto te estás cargando todo el DDD y estás volviendo a hacer un sistema CRUD. Como el repositorio no sabe que carajo ha cambiado el agregado (se ha perdido la pretensión de la operación);  lo único que puede hacer es obtener todo el estado interno del agregado y plantarlo en persistencia; incluida información que sólo se ha usado para comprobar las invariantes o realizar cálculos pero que no han cambiado después de la operación. Esto es igual al típico sistema CRUD donde la vista editable se pasa a piñón a persistencia porque no sabemos que quería hacer el usuario más allá de cambiar valores a campos que le presentamos en la vista.

Otra consecuencia perniciosa es que el agregado tiene que exponer en getter(s) su estado interno para que sea recuperado por el repositorio; el repositorio necesita ciertos conocimiento sobre como está implementado el agregado y el agregado los filtra. Nos hemos cargado de un plumazo la abstracción de la implementación, el bajo acoplamiento, la alta cohesión, el Tell Don't Ask y yo qué sé cuántas cosas más.

Dejo para la siguiente entrada la respuesta a la pregunta ¿Y entonces que coj**** hago? con una pista:

“I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is messaging.” ~ Alan Kay

3 comentarios:

  1. No sabes tú la de discusiones que he tenido con este tema, que me dedicaba a crear un dominio que se representaba en por lo mínimo 5 agregados distintos pero que acababan en la misma tabla por temas de legado. Y cosas más bizarras (un agregado que debía persistirse en 2 esquemas diferentes de tablas en la misma bd) y todo con NHibernate. Será más o menos lento pero qué maravilla/el mal encarnado (para bien) se puede hacer con ese ORM.

    ResponderEliminar
    Respuestas
    1. Es verdad que con NHibernate tienes una flexibilidad muy guapa a la hora de mapear una clase a "cachos" de la BD. Esta muy cerca de eliminar de verdad el desajuste de impedancia. Pero todo el mapeo "a manubrio" que tienes que montar si quieres tener bien segregadas las clases pertnenecientes a los Bounded Context es un curro de chinos y aun asi tienes un acoplamiento entre las clases y persistencia demasiado alto para mi gusto.

      Me quedo con mi solucion de que la persistencia simplemente gestione los eventos que representan los cambios en el sistema :)

      Eliminar
    2. Ah, simplemente comentaba una experiencia que en aquel momento era o eso o vivir en el paleolítico. Ya te digo si fue currazo de prueba y error, aprendí un montón con ello. Lo peor de estas situaciones es que acabas siendo el único capaz de trabajar con estas cosas y eso es muchisimo más duro que la dificultad de NHibernate. En el fondo es lo que me pasa hoy en día aún con Event Sourcing o demás soluciones de software... Si eres el único que las conoces/preocupas por entender, el camino será una puta mierda desagradecido. Pero luchar contra eso es nuestra misión. Al menos la mía.

      Eliminar