Blog Jose Cullar

//Scrum Master, Tech Lead & Full Stack Developer

Domain-Driven Design. Services & Domain Events

por Jose el 6 octubre, 2016

Services

Podemos definir los servicios como procesos que realizan determinadas tareas. Empleados y evolucionados desde Service Oriented Architecture o Remote Procedure Call. Tareas o acciones genéricas que no se asocian a una única determinada única instancia de objeto, de modo que la tendencia más habitual es crear métodos estáticos sobre la entidad o agregado. Esta práctica no se considera óptima por no seguir los principios de desarrollo y dificultando en gran medida el testeo, además de considerarse mala práctica acceder a repositorios dentro de los agregados o entidades en el modelo de dominio. La necesidad de incluir métodos estáticos en el modelo de dominio es un buen indicador para crear un servicio.

Application Services

  • Cliente directo de Domain Services y modelo de dominio.
  • Casos de uso de la aplicación que coordina y orquesta las peticiones a la lógica de negocio y repositorios.
  • Coordina las responsabilidades del modelo de dominio y los servicios de dominio.
  • Alojados en Application Layer.

Domain Services

  • Contiene la lógica/reglas de negocio.
  • Transforma un objeto de dominio a otro.
  • Calcula el valor mediante la entrada de objetos del modelo de dominio.
  • Pueden acceder a repositorios.
  • Alojados en Domain Layer.

Con objetivo de seguir los principios de desarrollo, declararemos interfaces para cada Servicio.
Los servicios no son una bala de plata, si los utilizamos en exceso extrayendo toda la lógica de aplicación o dominio en servicios, podemos ocasionar un Anemic Domain Model. Debe determinarse y decidirse correctamente si debe incluirse en el modelo de dominio un método de entidad/agregado o crear un servicio siguiendo en todo momento Single Responsability Principle.

Por normal general las implementaciones de los servicios se alojan en la capa de infraestructura, aunque en ocasiones y cuando tengamos una única implementación pueden alojarse en la misma capa donde se declara la interfaz.

Domain Events

Normalmente se utilizan procesos batch que intentan detectar cambios de estado sucedidos en un determinado intervalo de tiempo en el modelo de dominio, mediante grandes queries en la base de datos, para posteriormente realizar las acciones pertinentes mediante pesadas transacciones y modificaciones de datos. Normalmente procesos nocturnos largos y costosos.

Los Domain Events, capturan y notifican un determinado evento dentro del modelo de dominio mediante información representada en un objeto. De modo que podemos realizar las acciones pertinentes o aplicar un resultado determinado en el mismo momento que sucede el cambio de estado, evitando acumularlos en el tiempo esperando a detectarlos mediante dichos pesados y largos procesos nocturnos.

Los eventos sucedidos o cambios de estado de entidades o eventos pueden almacenarse a medida que vayan aconteciendo para disponer de un histórico de eventos, aplicando así Event Sourcing. Además, nos facilitan la integración de Bounded Contexts desacoplando estructuras o sistemas dentro de una organización.

Todos los sucesos no deben considerarse como eventos a notificar en el modelo de dominio, aconsejando hablar cuidadosamente con los expertos de dominio para identificarlos correctamente. Los eventos deben de formar parte del lenguaje ubicuo.

Las objetos que almacenan la información de un evento deben ser verbos en pasado con la propiedad mínima aconsejada de OccurrenceOn con la fecha/hora exacta de cuando sucede y notifica. Además necesitaremos la información necesaria relacionada con el evento, como los identificadores de las entidades que componen el agregado. Los eventos son diseñados normalmente como inmutables de igual modo que un Value Object. A pesar que sean inmutables, en algunos casos es necesario establecer al evento un identificador.

Command Operation: Backlogitem # CommitTo
Event outcome: BacklogItemCommitted.

Command Operation: Backlogitem # PlanTo
Event outcome: BacklogItemPlanned.




Fuente: Modeling Aggregates with DDD and Entity Framework.

Debemos disponer de uno o varios middleware que gestione los eventos, sea mediante mensajería siguiendo publish-subscribe pattern o mediante API REST (mediante un endpoint se realiza el GET por paquetes según estado para listar las notificaciones de los clientes que publican los eventos en la misma API).

Existen varias herramientas de mensajería que nos permitirán trabajar con eventos de forma sencilla, adoptando la consistencia eventual: RabbitMQ, Akka, NServiceBus, MassTransit.

La regla fundamental en la que basarnos para evitar realizar varias transacciones en un mismo evento, es modificar un único agregado por una única transacción realizada. En dicha transacción de agregado pueden modificarse los estados de todas las entidades asociadas mediante sus identificadores informados en el evento. Ya que se trata de una consistencia eventual y no inmediata, asumimos un delay en el resultado de la transacción. Aunque existen diversas técnicas para evitar que influya en la experiencia de usuario.

Normalmente los eventos se publican en los métodos de agregados o entidades en el modelo de dominio o en servicios de dominio, guardándolos si fuese el caso en el Event Store que corresponda. Todo ello mediante la infraestructura de mensajería según el middleware que utilicemos en cada caso. Por otra parte la subscripción de evento se realiza y recomienda en los servicios de aplicación, quien manejará la transacción a efectuar ya que se trata de un concepto de aplicación siempre y cuando no concierna a otros Bounded Context.




Fuente: Event sourcing the past and today.

Los Domain Events pueden utilizarse para la integración entre Bounded Context, alejándose del concepto de aplicación en el Bounded Context local. De este modo sería necesario registrar las subscripciones a eventos generados en otros Bounded Context en los servicios de dominio en vez de subscribirlos en los servicios de aplicación.

Event Store / Event Sourcing

Manteniendo almacenados todos los eventos de dominio disfrutaremos de las siguientes ventajas:

  • La posibilidad de disponer de un feed mediante una API REST de notificación a clientes
  • Examinar y disponer de un histórico de cambios de estado que puede ayudarnos con el debug y control/seguimiento de incidencias.
  • Analizar los datos para determinar el comportamiento y actividad de nuestros usuarios u otros aspectos relevantes que nos ayudarán en la toma de decisiones.
  • Producir screenshots por cada cambio de estado, que nos permitirán reconstruir agregados o una instancia en un momento concreto del tiempo. Pudiendo forzar nuevos eventos y cambios de estado para solventar bugs dentro de nuestro Event Stream.

La serialización del evento es la técnica recomendada y más utilizada para el almacenamiento de toda la información del evento en su correspondiente Event Store.

Event de-duplication

En ciertos escenarios podría ser necesario comprobar la duplicación en la recepción de los mensajes publicados, ya que podrían enviarse y procesarse más en más de una ocasión. Para prevenir las consecuencias, dado este escenario/contexto, es necesario que las transacciones se realicen mediante idempotence operation pudiendo ser ejecutada más de una vez veces con resultados idénticos. Pueden identificarse y detectarse eventos por un campo específico o detectar duplicaciones de eventos mediante versionado.

Lecturas recomendadas:

Rendimiento con Domain Events, Proyecciones y principios de CQRS
Domain Events and Eventual Consistency
Domain Events – Domain Events allow you to segregate the models of different systems
Domain Event – Martin Fowler
Vaughn Vernon on Microservices and Domain-Driven Design

En episodios anteriores…

Domain-Driven Design. Episodio I. Empezando…
Domain-Driven Design. Episodio II. Context Maps
Domain-Driven Design. Episodio III. Arquitectura
Domain-Driven Design. Episodio IV. Entities & Value Objects

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *