Aunque hemos simplificado la carga que supone recuperar e hidratar un Aggregate root, todavía hay un Use case que tiene un volumen importante de peticiones y debe ser optimizado.
Abriendo debate técnico
Si con nuestra BBDD actual y nuestros esfuerzos por conseguir la información usta sin pasar por hidratar objetos complejos como un Aggregate root no es suficiente… Empezamos a pensar que nuestro motor de retorno de información se debe especializar. Debemos separar por completo a nivel de tecnología de almacenamiento el concepto de Query (representado por el Read model) del concepto de Command (representado por el Write model).
Si hacemos esto, podemos usar tecnologías pensadas para leer con más rapidez, como Elasticsearch.
Agregando rendimiento de lectura al caso de uso. Salto tecnológico usando Elasticsearch
Sacamos otra carta de la baraja de tipo Ability llamada Search. Esta carta está especializada en buscar información de forma ágil. Decidimos sustituir Persistence por Search. Ya tenemos resuelta la intención. Nuestro Bounded context debe tener una Ability capaz de buscar de forma rápida. Pero todavía no sabemos cómo implementar esta solución. Para este caso, decidimos que la tecnología a utilizar es ElasticSearch, con lo que colocamos la carta Implementation en su lugar, justo debajo de Search.
Un Query use case con Read model usando ElasticSearch nos quedaría así…
Debate sobre cómo hacer que Search y Persistence trabajen juntos
Parece que ya tenemos resuelto el problema. Ya tenemos una solución híbrida de lectura. Pero esto nos abre otro debate… Para poder usar Search, de algún sitio se tiene que alimentar la información de esta carta. Persistence tiene una tecnología, pero Search tiene otra. Persistence es bueno guardando los Aggregates cuidando su coherencia, mientras que Search es buena obteniendo la información de esos Aggregates, y aparte, usa otra tecnología que no se preocupa de la coherencia de una forma tan celosa como Persistence. Necesitamos una forma de propagar la información de una tecnología a la otra para que el Read model funcione como concepto…
Ahora si… el melón de Read model está abierto del todo… XD
Abriendo debate técnico sobre alimentar el Read model
Si necesitamos un sistema que nos guarde la entidad con seguridad y coherencia y otro sistema que se especializa en ofrecer una perspectiva de la misma información orientada a los casos de uso que necesitan dicha información de forma ágil, llegamos a la conclusión que necesitamos “algo” que mantenga los dos sistemas alineados.
Hay varias estrategias. Las más habituales son:
-
Trigger en BBDD que realice una acción (alias “tenemos un tigre en la base de datos”), ya que ocultamos lógica en la BBDD.
-
Crear una vista en base de datos que nos de el Read model ya cocinado usando los datos de nuestro Aggregate root. Es similar al concepto de trigger. Movemos cierta funcionalidad a la base de datos, pero es más fácil de controlar.
-
Hacer que el Use case de tipo Command maneje los dos sistemas al persistir el Aggregate root.(Ojito con que el Use case falle y ya haya guardado algo en el Read model). Este escenario sencillo de implementar nuestro Aggreate root y la proyección se guardan en la misma base de datos y hay una transacción abierta (poco común). Si no, es algo complejo de gestionar para poder deshacer el Read model en caso de fallo.
-
Implementar Domain events en los Command use case y a su implementar Console commands para actualizar el Read model. Esta aproximación da más trabajo porque requiere más pasos, pero es la más segura de implementar y según como gestionemos los Domain events, podremos comunicarnos con otros Bounded context.
Hay más estrategias de las que acabamos de ver, no las vamos a comentar todas. Sólo hay que saber qué se gana y qué se pierde en cada una de ellas.