Suscripción a Eventos de Dominio
En este tutorial, exploraremos cómo un microservicio puede suscribirse a eventos de dominio publicados por otros microservicios y reaccionar a ellos. Crearemos un nuevo microservicio llamado Subscriptions que publicará un evento SubscriptionCanceledDomainEvent
. Luego, modificaremos el microservicio de Clients para escuchar este evento e inactivar al cliente correspondiente. Utilizaremos el generador codedesignplus:microservice
para crear el microservicio Subscriptions y agregar soporte para consumidores de eventos al microservicio Clients.
¿Por qué Suscribirse a Eventos?
La suscripción a eventos permite que los microservicios se comuniquen de manera asíncrona y desacoplada. En lugar de realizar llamadas directas (como en REST o gRPC), un microservicio puede publicar un evento y otros microservicios interesados en ese evento pueden reaccionar a él. Esto ofrece:
- Desacoplamiento: Los microservicios no dependen directamente unos de otros.
- Escalabilidad: Permite que cada microservicio opere a su propio ritmo.
- Resiliencia: Si un microservicio no está disponible, los eventos aún pueden ser procesados cuando el servicio vuelva a estar activo.
- Extensibilidad: Es fácil agregar nuevos consumidores sin modificar los servicios que publican los eventos.
Prerrequisitos
Para seguir este tutorial, necesitarás:
- Tener creado el microservicio Clients siguiendo el tutorial anterior.
- Crear el microservicio Subscriptions utilizando el generador
codedesignplus:microservice
y publicar el eventoSubscriptionCanceledDomainEvent
. - Tener funcionando el entorno de desarrollo descrito en el tutorial de Entorno de desarrollo debido a que se requiere de RabbitMQ para la comunicación entre microservicios.
¿Qué Aprenderás?
- Cómo añadir soporte para consumidores de eventos a un microservicio existente utilizando el generador
codedesignplus:microservice
. - Cómo implementar un consumidor para escuchar y reaccionar a un evento específico.
Creando el Microservicio Subscriptions
En este punto, no profundizaremos en la creación del microservicio, ya que hemos cubierto este proceso en tutoriales anteriores. Utilizaremos el generador codedesignplus:microservice
para crear el microservicio Subscriptions y agregar soporte para publicar eventos de dominio.
-
Abre tu terminal y ejecuta el siguiente comando para crear el microservicio Subscriptions:
Terminal window yo codedesignplus:microservice microservice \--organization "Acme" \--microservice "Subscriptions" \--description "Microservicio para gestionar las suscripciones de la plataforma." \--contact-name "Jane Doe" \--contact-email "jane.doe@example.com" \--vault store-acme \--is-crud \--aggregate "Subscription"Terminal window yo codedesignplus:microservice microservice `--organization "Acme" `--microservice "Subscriptions" `--description "Microservicio para gestionar las suscripciones de la plataforma." `--contact-name "Jane Doe" `--contact-email "jane.doe@example.com" `--vault store-acme `--is-crud `--aggregate "Subscription" -
Crea el evento de dominio
SubscriptionCanceledDomainEvent
que será publicado cuando se cancele una suscripción.using Acme.Net.Microservice.Subscriptions.Domain.Enums;namespace Acme.Net.Microservice.Subscriptions.Domain.DomainEvents;[EventKey<SubscriptionAggregate>(1, "SubscriptionCanceledDomainEvent")]public class SubscriptionCanceledDomainEvent : SubscriptionDomainBaseEvent{private SubscriptionCanceledDomainEvent(Guid aggregateId,Guid? eventId = null,Instant? occurredAt = null,Dictionary<string, object>? metadata = null) : base(aggregateId, eventId, occurredAt, metadata){}public static SubscriptionCanceledDomainEvent Create(Guid aggregateId,Guid idClient,Guid idProduct,SubscriptionStatus status,Instant startDate,Instant endDate,Guid tenant,Guid createdBy,Instant createdAt,Guid? updatedBy,Instant? updatedAt){return new SubscriptionCanceledDomainEvent(aggregateId){IdClient = idClient,IdProduct = idProduct,Status = status,StartDate = startDate,EndDate = endDate,Tenant = tenant,CreatedBy = createdBy,CreatedAt = createdAt,UpdatedBy = updatedBy,UpdatedAt = updatedAt};}} -
Modifica el aggregate
Subscription
para lanzar el eventoSubscriptionCanceledDomainEvent
cuando se cancele una suscripción.using Acme.Net.Microservice.Subscriptions.Domain.Enums;namespace Acme.Net.Microservice.Subscriptions.Domain;public class SubscriptionAggregate(Guid id) : AggregateRoot(id){public Guid IdClient { get; private set; }public Guid IdProduct { get; private set; }public Instant StartDate { get; private set; }public Instant EndDate { get; private set; }public SubscriptionStatus Status { get; private set; }private SubscriptionAggregate(Guid id, Guid idClient, Guid idProduct, Instant startDate, Instant endDate, Guid tenant, Guid createdBy) : this(id){IdClient = idClient;IdProduct = idProduct;StartDate = startDate;EndDate = endDate;Status = SubscriptionStatus.Active;Tenant = tenant;CreatedBy = createdBy;CreatedAt = SystemClock.Instance.GetCurrentInstant();AddEvent(SubscriptionCreatedDomainEvent.Create(Id, IdClient, IdProduct, Status, StartDate, EndDate, Tenant, CreatedBy, CreatedAt, UpdatedBy, UpdatedAt));}public static SubscriptionAggregate Create(Guid id, Guid idClient, Guid idProduct, Instant startDate, Instant endDate, Guid tenant, Guid createdBy){DomainGuard.GuidIsEmpty(id, Errors.InvalidId);DomainGuard.GuidIsEmpty(idClient, Errors.InvalidIdClient);DomainGuard.GuidIsEmpty(idProduct, Errors.InvalidIdProduct);DomainGuard.GuidIsEmpty(tenant, Errors.InvalidTenant);DomainGuard.GuidIsEmpty(createdBy, Errors.InvalidCreatedBy);DomainGuard.IsTrue(startDate == Instant.MinValue, Errors.InvalidStartDate);DomainGuard.IsTrue(endDate == Instant.MinValue, Errors.InvalidEndDate);DomainGuard.IsTrue(startDate > endDate, Errors.StartDateGreaterThanEndDate);return new SubscriptionAggregate(id, idClient, idProduct, startDate, endDate, tenant, createdBy);}public void Update(Guid idProduct, Instant startDate, Instant endDate, Guid updatedBy){DomainGuard.GuidIsEmpty(idProduct, Errors.InvalidIdProduct);DomainGuard.GuidIsEmpty(updatedBy, Errors.InvalidUpdatedBy);DomainGuard.IsTrue(startDate == Instant.MinValue, Errors.InvalidStartDate);DomainGuard.IsTrue(endDate == Instant.MinValue, Errors.InvalidEndDate);DomainGuard.IsTrue(startDate > endDate, Errors.StartDateGreaterThanEndDate);IdProduct = idProduct;StartDate = startDate;EndDate = endDate;UpdatedBy = updatedBy;UpdatedAt = SystemClock.Instance.GetCurrentInstant();AddEvent(SubscriptionUpdatedDomainEvent.Create(Id, IdClient, IdProduct, Status, StartDate, EndDate, Tenant, CreatedBy, CreatedAt, UpdatedBy, UpdatedAt));}public void CancelSuscription(Guid updatedBy){DomainGuard.GuidIsEmpty(updatedBy, Errors.InvalidUpdatedBy);Status = SubscriptionStatus.Canceled;UpdatedBy = updatedBy;UpdatedAt = SystemClock.Instance.GetCurrentInstant();AddEvent(SubscriptionCanceledDomainEvent.Create(Id, IdClient, IdProduct, Status, StartDate, EndDate, Tenant, CreatedBy, CreatedAt, UpdatedBy, UpdatedAt));}public void InactivateSubscription(Guid updatedBy){DomainGuard.GuidIsEmpty(updatedBy, Errors.InvalidUpdatedBy);Status = SubscriptionStatus.Inactive;UpdatedBy = updatedBy;UpdatedAt = SystemClock.Instance.GetCurrentInstant();AddEvent(SubscriptionInactivatedDomainEvent.Create(Id, IdClient, IdProduct, Status, StartDate, EndDate, Tenant, CreatedBy, CreatedAt, UpdatedBy, UpdatedAt));}}
Agregando Soporte para Consumidores de Eventos
Ahora, vamos a modificar el microservicio Clients para que se suscriba al evento SubscriptionCanceledDomainEvent
. Utilizaremos el generador codedesignplus:microservice
con la opción asyncWorker
.
-
Abre tu terminal y navega al directorio raíz del microservicio de Clients. Ejecuta el siguiente comando:
Terminal window yo codedesignplus:microservice asyncWorker -
El generador agregará los proyectos AsyncWorker necesarios para consumir eventos de dominio.
-
Ejecutaremos el siguiente comando para agregar el evento
SubscriptionCanceledDomainEvent
, el aggregateSubscription
y el comandoCancelSubscriptionCommand
al microservicio Clients.Terminal window yo codedesignplus:microservice consumer \--consumer-name "SubscriptionCanceled" \--consumer-aggregate "Subscription" \--consumer-action "inactive_client" \--consumer-microservice "ms-suscriptions"Terminal window yo codedesignplus:microservice consumer `--consumer-name "SubscriptionCanceled" `--consumer-aggregate "Subscription" `--consumer-action "inactive_client" `--consumer-microservice "ms-suscriptions"Al ejecutar el comando, se creará la estructura necesaria para consumir el evento
SubscriptionCanceledDomainEvent
en el microservicio Clients.Los archivos generados son:
-
Domain Event
SubscriptionCanceledDomainEvent.cs
:Este archivo representa el evento
SubscriptionCanceledDomainEvent
que será consumido por el microservicio Clients. -
Consumer
SubscriptionCanceledHandler.cs
:Este archivo recibe el evento
SubscriptionCanceledDomainEvent
e invoca el command handlerInactiveClientCommandHandler
-
Command:
InactiveClientCommand.cs
:Este archivo representa el comando
InactiveClientCommand
que será utilizado para invocar el command handlerInactiveClientCommandHandler
-
Command Handler:
InactiveClientCommandHandler.cs
:Este archivo representa el command handler
InactiveClientCommandHandler
que será invocado por el consumerSubscriptionCanceledHandler
para inactivar al cliente correspondiente. -
Aggregate:
Subscription.cs
:Este archivo representa el agregado
Subscription
que será utilizado para persistir la información de los eventos consumidos y procesados. -
Repository:
SubscriptionRepository.cs
yISubscriptionRepository.cs
:Este archivo representa el repositorio
SubscriptionRepository
que será utilizado para persistir la información del agregadoSubscription
.
-
Consumiendo Eventos de Dominio
Ahora que hemos agregado soporte para consumidores de eventos al microservicio Clients, vamos a implementar la lógica necesaria para inactivar al cliente correspondiente cuando se cancele una suscripción.
-
Abre el archivo
SubscriptionCanceledDomainEvent.cs
y agrega las propiedades necesarias para mapear la informacaión del evento. Recuerda que el evento de dominio tiene una propiedadSubscriptionStatus
que indica el estado de la suscripción, esta sera creada en la capa de dominio debido a que tambien sera usada por el aggregateSubscription
. -
Modificaremos el aggregate
Suscription
para persitir solo la información necesaria del eventoSubscriptionCanceledDomainEvent
y que requiera la lógica de negocio. -
Implementaremos el command
InactivateClientCommand
que será utilizado para inactivar al cliente correspondiente. -
Implementaremos el command handler
InactiveClientCommandHandler
que será invocado por el consumerSubscriptionCanceledHandler
para persistir la información de la suscripción cancelada e inactivar al cliente correspondiente invocando el commandInactivateClientCommand
que pertenece al microservicio Clients. -
Modificaremos el consumer
SubscriptionCanceledHandler
para invocar el command handlerInactiveClientCommandHandler
cuando se reciba el eventoSubscriptionCanceledDomainEvent
.
Conclusiones
En este tutorial, hemos aprendido cómo crear un microservicio que publica eventos de dominio y cómo otro microservicio puede suscribirse a ellos. Hemos utilizado el generador codedesignplus:microservice
para agilizar la creación de estos componentes y hemos visto cómo la arquitectura basada en eventos permite una comunicación asíncrona y desacoplada entre microservicios.
Este es un paso clave para construir sistemas robustos y escalables basados en microservicios.