Message Bus

Construyendo un Bus de Comandos

Con seguridad existen varios paquetes disponibles que implementan un bus de comandos con distinto nivel de complejidad y funcionalidad, sin embargo si deseas implementar algo sencillo o tener este componente totalmente en tu control, a continuación mostraré un ejemplo que puede servir como base.

La infraestructura de un bus de comandos propuesta estará compuesta por tres componentes principales:

  1. Comandos, que son objetos de datos que se pasan al bus de comandos
  2. Controladores (Handlers), que se ocupan de receptar un comando y completar la tarea requerida.
  3. Bus de comandos, que es responsable de despachar comandos a los controladores (handlers) y crear instancias de los objetos necesarios.

Está implmenetación no es muy diferente a la implementación de un bus de eventos, sin embargo la gran diferencia es que los comandos tienen un solo controlador, mientras que los eventos tienen uno o varios controladores.

La interfaz Command

Lo primero que necesitamos crear es la interfaz Command

type Command interface {
}

La interfaz Command no tiene definiciones de métodos, dado que por ahora un Comando es básicamente un objeto plano que contiene datos.

Pero, ¿entonces, para qué creamos la interfaz? Existe mucho valor en implementar interfaces, incluso si la interfaz no contiene ningún método dado que hace que el código sea mucho más legible y fácil de entender para otros desarrolladores.

La interfaz Handler

Ahora, es necesario definir la interfaz Handler:

type Handler interface {
 handle(command Command)
}

Cada controlador requiere implementar un método handle(), que debería aceptar un comando (Command). Realmente no nos importa cómo maneja el controlador al comando, solo nos importa decirle qué hacer.

El contenedor

El bus de comandos será responsable de crear una nueva instancia de un controlador particular para cada comando que reciba, esto significa que el bus de comandos requiere una manera de crear instancias de otros objetos.

Vamos a definir una interfaz Container que esté en nuestro control con la finalidad de poder realizar implementaciones distintas que satisfagan nuestro contrato.

type Container interface {
 make(typeName string)
}

El inflector

El bus de comandos también puede ser responsable de asignar los comandos con su respectivo controlado, sin embargo en lugar de acoplar esta responsabilidad en el bus de comandos, podemos escribir un inflector que pueda hacer el trabajo por nosotros.

type Inflector interface {
 inflect(command Command)
}

Una implementación sencilla del inflector podría ser la siguiente:

func (ni *NameInflector) Inflect(command Command) string {
 t := reflect.TypeOf(command)
 h := t.Elem().Name()
 return strings.Replace(h, "Command", "Handler", -1)
}

Implementacion del bus de comandos

Finalmente vamos a implementar el bus de comandos. Al bus de comandos se le deben inyectar instancias de una implementación de Container y otra de una implementación de Inflector, adicionalmente debe tener un método execute() que acepte un Command.

type CommandBus struct {
 Container Container
 Inflector Inflector
}
func (cb *CommandBus) handler(c Command) Handler {
 name := cb.Inflector.Inflect(c)
 return cb.Container.make(name).(Handler)
}
func (cb *CommandBus) Execute(c Command) {
 cb.handler(c).Handle(c)
}

Como se puede ver, el método execute() no devuelve ningún valor. Adicionalmente contiene un método privado para determinar qué handler utilizar y luego buscarlo en el contenedor.

Notas finales

La capa de aplicación en DDD es una parte más de nuestra arquitectura. Esta capa maneja la comunicación con el mundo exterior aceptando solicitudes y respondiendolas. Al actuar con un componente delimitador en realidad no le importa de dónde viene la solicitud o hacia dónde va.

Hay un par de enfoques frecuentemente utilizados para construir la capa de aplicación. Dos opciones populares son los Servicios de Aplicación o el uso de Comandos y Manejadores. Ambos enfoques tienen aspectos positivos y negativos, por lo que depende de la necesidad decidir cuál es el adecuado para el problema en cuestión.

Este artículo muestra una implementación simple del bus de comandos, sin embargo puede ser útil como base para robustecer una solución más específica o simplemente para entender los conceptos relacionados. 

Deja una respuesta

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

Blue Captcha Image
Refrescar

*