miércoles, 1 de agosto de 2018

Hidratar agregado con event sourcing.

Esta entrada, al igual que las anteriores (las que van entre ésta y ésta), es generada por la conversación que tuve con unos desarrolladores de software hace no mucho.

Cuando comentábamos los pormenores de la implemntación de un pequeño test de código, uno de ellos hizo un comentario con terribles implicaciones; puesto que denota una falta de profundidad en el entendimiento y comprensión sobre agregados, eventos y event sourcing (ES).

El comentario fue: "Esto como agregado... no pinta bien." o algo así. Este comentario implica tantísimas cosas erróneas en los cimientos necesarios para aplicar correctamente agregados, eventos y event sourcing que no sé ni por donde empezar... pero lo voy a intentar.

Lo primero es que sin que se solicite explícitamente nadie va a diseñar un agregado hidratado por ES cuando programa el típico test de código de HackerRank o Codility; es un algoritmo y punto, no tiene más chicha que eso. Y lo segundo es que NO es posible. Si le pegáis un vistazo a la entrada de este blog sobre el test veréis que el input no son eventos por lo que es demencial pensar que con eso se puede hacer algo de agregados y ES.

Si el agregado de Factura tiene que aplicar un complejo algoritmo que necesita una estructura de datos Diccionario y tiene que recalcular la información a partir de datos sin estado estamos diseñando tremendamente mal los agregados y el ES.

ES no significa recalcular el estado de todo ciegamente a partir de los datos sin ningún precalculo ni aplicación de ninguna regla de dominio; significa estado incremental a partir de estados más pequeños y contextualizados (los eventos). Los eventos tienen estado; un estado que, si se aplica de forma incremental en un agregado, refleja una consistencia en el dominio bien definida.

Leeros la entrada del test de código venga. Os espero.

¿Ya? Pues vamos a hablar de ello.

Pongamos sobre la mesa el agregado Factura y una operación de GenerarFactura. Para hidratar este agregado con ES debemos alimentarlo con eventos de trabajos realizados; parecido al test de código pero no igual. Los eventos de trabajos realizados deben tener un estado; que es el precio ya calculado de ese trabajo. Ese evento lo generaría un agregado Trabajo con su operación RegistrarTrabajoRealizado; es al registrar el trabajo realizado cuando el agregado calcula el precio que le corresponde a ese trabajo y lanza el evento TrabajoRealizado con su precio.

Ya nos hemos quitado de en medio el cálculo de precio por trabajo en el agregado Factura.

Con respecto a la necesidad de tener el trabajo acumulado más largo identificado para restarlo y aplicar la oferta a la factura, la cosa esta clara: Para el agregado Facturael trabajo más largo es un Value Object (VO) que se usa para construir el agregado. Y el agregado simplemente lo utiliza para realizar la resta al precio final.

Resumiendo; el agregado Factura se hidrata con el VO trabajo acumulado más largo y los eventos de trabajos registrados con su precio. Por lo que el agregado simplemente tiene que ir aplicando los eventos en su estado interno (empieza con precio 0 y va sumando los precios de los trabajos incluidos en los eventos) y luego aplicar la oferta de restar el trabajo acumulado más largo. Una vez finalizado, este agregado lanza el evento FacturaGenerada con el precio final de la factura.

¿Y como se obtiene el VO trabajo acumulado más largo en ES? 

Pues depende mucho de las necesidades del dominio, rendimiento, etc.

En el caso más simple podemos tener directamente ese dato precalculado en persistencia sin miedo; aunque algún talibán del ES nos diga que eso no es ES. Ni caso oye. Es un VO sin identidad que no recibe cambios por referencia, si no directos.

También podemos tener un SQL o MapReduce que utilice los eventos de persistencia de TrabajoRegistrado para calcularlo. RavenDB es una maravilla en este caso y te permite tener predefinidos estos MapReduce en el repositorio; los cuales se lanzan automáticamente cuando la fuente de datos cambie y no tienes que "ensuciar" el esquema de datos de ES con VO's precalculados.

Otra opción es que el encargado de construir e hidratar el agregado Factura se encargue de calcular este VO para pasárselo al agregado. Ahí no tendría problemas para usar un Diccionario e ir acumulando los trabajos y obtener el resultado.

Existen más opciones y más complejas, mayormente por necesidades del dominio, que no voy a exponer aquí, pero seguro que ya os habéis hecho una idea sobre que, si no surgen necesidades más enrevesadas, esto es un VO y no hay que complicarse la vida más de lo necesario.

¿Entendéis ahora por qué un comentario tan corto y tan sencillo implica tantos errores de concepción y diseño? Indica que estás un poco verde en temas absolutamente indispensables para una arquitectura decente DDD con ES; que no sabes bien como diseñar conceptualmente los agregados, los VO's y los eventos; que no sabes bien como aplicar ES y utilizarlo para rehidratar los agregados. Implica que no tienes bien asentados los cimientos necesarios y la arquitectura que estás construyendo tiene los pies de barro.

No hay comentarios:

Publicar un comentario