Skip to content

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:

  1. Tener creado el microservicio Clients siguiendo el tutorial anterior.
  2. Crear el microservicio Subscriptions utilizando el generador codedesignplus:microservice y publicar el evento SubscriptionCanceledDomainEvent.
  3. 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?

  1. Cómo añadir soporte para consumidores de eventos a un microservicio existente utilizando el generador codedesignplus:microservice.
  2. 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.

  1. 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"
  2. 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
    };
    }
    }
  3. Modifica el aggregate Subscription para lanzar el evento SubscriptionCanceledDomainEvent 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.

  1. Abre tu terminal y navega al directorio raíz del microservicio de Clients. Ejecuta el siguiente comando:

    Terminal window
    yo codedesignplus:microservice asyncWorker
    Suscripción Microservicio Clients
  2. El generador agregará los proyectos AsyncWorker necesarios para consumir eventos de dominio.

    Program Microservicio Clients
  3. Ejecutaremos el siguiente comando para agregar el evento SubscriptionCanceledDomainEvent, el aggregate Subscription y el comando CancelSubscriptionCommand al microservicio Clients.

    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.

    Consumer 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 handler InactiveClientCommandHandler

    • Command: InactiveClientCommand.cs:

      Este archivo representa el comando InactiveClientCommand que será utilizado para invocar el command handler InactiveClientCommandHandler

    • Command Handler: InactiveClientCommandHandler.cs:

      Este archivo representa el command handler InactiveClientCommandHandler que será invocado por el consumer SubscriptionCanceledHandler 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 y ISubscriptionRepository.cs:

      Este archivo representa el repositorio SubscriptionRepository que será utilizado para persistir la información del agregado Subscription.

    InactiveClientCommandHandlerClients MSRabbitMQSubscriptions MSInactiveClientCommandHandlerClients MSRabbitMQSubscriptions MSPublish SubscriptionCanceledDomainEventSubscriptionCanceledDomainEventSubscriptionCanceledHandlerInactiveClientCommandInactive Client

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.

  1. 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 propiedad SubscriptionStatus que indica el estado de la suscripción, esta sera creada en la capa de dominio debido a que tambien sera usada por el aggregate Subscription.

    SubscriptionCanceled
  2. Modificaremos el aggregate Suscription para persitir solo la información necesaria del evento SubscriptionCanceledDomainEvent y que requiera la lógica de negocio.

    SubscriptionAggregate
  3. Implementaremos el command InactivateClientCommand que será utilizado para inactivar al cliente correspondiente.

    InactiveClientCommand
  4. Implementaremos el command handler InactiveClientCommandHandler que será invocado por el consumer SubscriptionCanceledHandler para persistir la información de la suscripción cancelada e inactivar al cliente correspondiente invocando el command InactivateClientCommand que pertenece al microservicio Clients.

    InactiveClientCommandHandler
  5. Modificaremos el consumer SubscriptionCanceledHandler para invocar el command handler InactiveClientCommandHandler cuando se reciba el evento SubscriptionCanceledDomainEvent.

    SubscriptionCanceledHandler

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.