La aventura de las Arquitecturas Event-Driven (Parte 2)

Publicado por Víctor Madrid el

event-drivenArquitectura de Soluciones

En este segundo artículo de la serie Arquitecturas Event-Driven continuaremos explicando el uso y las características que puede tener un EVENTO, para ello nos lo "curraremos" un poquito más y le dotaremos de funcionalidades más complejas.

Por lo tanto, si tienes alguna duda o no tienes algún concepto muy claro te aconsejo que leas el artículo anterior (Parte 1)  (ya que nos basaremos en él) y sobre todo porque daremos algunas cosas por supuestas.

Este artículo está dividido en 3 partes:

  1. Ampliación de las Características de un Evento
    1.1. Organizativas
    1.2. Internacionalización
    1.3. Ordenación
  2. Ampliación de Actores implicados en un Evento
  3. Conclusiones

1. Ampliación de las Características de un Evento

Todo evento puede requerir de funcionalidades extras

Las características que se van a abordar en este artículo son un poco peculiares de por sí: sirven para cubrir unos ámbitos muy concretos y además, pueden ser "reales" (aparecen declaradas en el propio evento) o bien "supuestas" (se presuponen dentro de su contexto... pero alguien te lo tiene que explicar), así que cuidado con las suposiciones. Recuerda que en el mundo de los negocios nunca se debe dar nada por supuesto.

¿Qué aspectos/conceptos puedo considerar como características extras de un evento?

Nosotros consideraremos para este artículo los marcados en negrita

  • Organizativas
  • Internacionalización
  • Ordenación
  • Inmutabilidad
  • Expiración
  • Ubicación
  • Seguridad
  • Modos de Ejecución

1.1. Organizativas

Organizar siempre se considera un aspecto muy importante en cualquier sitio y para cualquier cosa. ¿A quién le gusta el caos?, ¿no encontrar las cosas?, ¿y no saber diferencias algo del resto de elementos? ... jeje.

En este punto, se van a mostrar algunas propuestas de ayuda en la definición de un evento. Con ellas se pretende cubrir algunas de las consideraciones relacionadas con solucionar las preguntas anteriores y muchas más cosas.

El límite, como pasa siempre, será el nivel de detalle que se le quiera dar y las funcionalidades que se quieran cubrir.

Para ello se aconseja:

  • Establecer algún tipo de clasificación / taxonomía / criterio de filtrado / valores de pertenencia / etc ([typeCriteria] como puede ser eventType, objectType, taxonomy ...), todo esto con un sentido claro dentro de la compañía o el proyecto.
  • Existen diferentes enfoques:
  • name-based: Utilizar un nombre del evento "sensato" -> Para ello es importante tener una definición de naming global que esté bien definida y que todo el mundo entienda.
  • type-based: Incorporar uno o varios campos que establezcan el tipo de operación o de dominio de un evento.
  • object-based: Incorporar un campo que establezca el tipo de objeto sobre el que se aplica la operación que realiza el evento.
  • extra-based: Incorporar uno o varios campos extra que establezcan algún tipo de subtipología.
  • origin-based : Incorporar un campo que indique el origen de la información contenida como: Atómica o Derivada.
  • Atómica: Información recuperada "tal cual" desde su origen -> SIN ningún tipo de modificación.
  • Derivada: Información recuperada a partir de una información base (por ejemplo: reducción, transformación, etc.)
  • used-based: Incorporar un campo que indique si el evento tiene un uso sin tratamiento (no estandarizado o que se ha generado ad-hoc) o con tratamiento (estandarizado) dentro de una gobernanza de eventos.
  • El hecho de añadir algún aspecto sobre los elementos con tratamientos podría ayudar a entender las transformaciones o cambios sobre los que se ha visto implicado el evento
  • context-based: Incorporar un campo que indique el contexto y/o propósito del evento : técnico, dominio, business, etc.
  • scheme-based: Incorporar un campo que indique contra qué schema o lenguaje se va a utilizar -> Tened en cuenta la validación de formatos al hacer uso de esquemas.
  • Uso opcional de un Schema Registry
  • tracing-based: Incorporar un campo que permite hacer un seguimiento del evento (trazabilidad) que lo origino siempre y cuando este pueda generar o convertirse en otros eventos -> de esto también podemos hablar largo y tendido, ya sea indicando el flujo de eventos al que pertenece o bien algún aspecto de sus antecesores.
  • Se podrá indicar todo lo que hecho o ha pasado con un evento (Campos como traceId).
  • Híbrido entre las anteriores -> En este punto viene la locura si no se tiene todo controlado
  • ...
  • Las anteriores consideraciones deberían de ser aceptadas por toda la organización, áreas, equipos , etc.
  • Algunas propuestas de clasificación pueden ser por: proyecto, departamento, área, ...
  • Se puede utilizar uno o más enfoques a la vez.
  • No siempre viene determinado por un campo extra añadido. A  veces el propio evento o la zona donde se va a persistir durante su ciclo de vida, determinará su taxonomía o clasificación (por ejemplo el uso de un topic con una funcionalidad muy concreta).
  • Esta muy relacionado con la seguridad.
  • Establecer algún tipo de jerarquía de eventos (parentEventId,...) cuando sea necesario.
  • Para ello se incorporará el identificador del evento padre dentro del evento.
  • Esta referencia puede tener diferentes funcionalidades como: herencia, dependencia y/o relación (esto ya se detallará en cada caso).
  • Establecer algún tipo de marca evolutiva que permita descubrir lo que ha podido evolucionar un evento desde su origen, así como los eventos que tienen parentesco con el evento que los creo.
  • Cuando estas consideraciones funcionan más como opción de filtrado normalmente se define que pasará con los que cumplen la validación de forma positiva y que pasará con los que no la cumplen.

Muy raro, básico y/o particular sería el evento con el que vas a tener que trabajar para que no tuvieras que implementar algunas de estas propiedades, en el peor de los casos estoy seguro de que las utilizarás al menos una vez en tu vida :-)

Ejemplos de uso

Nota: Todos los ejemplos de uso irán centrados en considerar el envío de un email con payload en formato JSON (de aquí en adelante EEPJ-Envío Email Payload JSON) de esta forma podréis ver las diferencias "particulares" de cada caso. Tened en cuenta que estas propiedades pueden ser utilizadas en cada caso y que no se exige que sean exactamente como se representan en estos ejemplos.

  • Ejemplo 1: "EEPJ name-based": Evento EEPJ con enfoque name-based, donde el nombre ayuda un poquito a saber que esta pasando.
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL_CREATE_NEW_USER',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 2: "EEPJ type-based": Evento EEPJ con enfoque type-based, donde se ha añadido un campo type para clasificar de qué tipo de email se trata, por lo que podemos ver que es un evento de envío de email para la creación de un usuario (normalmente siempre se suele hacer referencia a algo que ya ha pasado "CREATED", pero podría tratarse de un evento anterior a su creación como puede ser un correo de confirmación)
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL',
  type : 'CREATE_NEW_USER',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 3: "EEPJ type-based avanzado": Evento EEPJ con enfoque type-based pero con más tipos, similar al anterior pero añade un campo extra de clasificación al tipo con el campo subtype, es decir, es un "envío de email" que reacciona a la creación de un nuevo usuario y permite diferenciarlo de un envío de email de creación de, por ejemplo, un email de una promoción.
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW_USER',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 4: "EEPJ object-based": Evento EEPJ similar al anterior y reutilizable en otros casos, únicamente incorpora el campo object que hace referencia sobre el objeto con el que trabaja. En este caso facilitaría diferenciar de forma mas fácil el caso anterior de un envío para usuario o para promoción. Proporcionaría por ejemplo la capacidad de averiguar/listar todos los eventos ocurridos para el objeto "USER" durante un rango de tiempo : CREATE, REMOVE, UPDATE, TRANSFORM, ...
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 5: "EEPJ origin-based atómico": Evento EEPJ similar al anterior se indica que es del tipo "ATOMIC" con el campo "origin", de esta forma se podría pensar que el campo payload tiene los datos tal cual los han añadido la aplicación al dar de alta el usuario. Este caso tiene mucho más sentido por ejemplo cuando se trabaja con la base de datos ya que permitiría distinguir si fueron los datos originales con los que se dio de alta lo que fuera o bien han podido realizar algún tipo de "manipulación".
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  eventDataFormat  : 'JSON',
  origin : 'ATOMIC'
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 6: "EEPJ origin-based derivado": Evento EEPJ similar al anterior se indica que es del tipo "DERIVADO" con el campo "origin", de esta forma se podría pensar que algunos de los campos NO son iguales a los que se crearon originalmente. Este caso sería lo contrario al "Ejemplo 5".
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  eventDataFormat  : 'JSON',
  origin : 'DERIVED',
  payload : '{
      to : 'destino@acme.com',
      from : '${email.generated}',
      subject : 'Importante',
      content : 'Test {1} {2} {3}',
      params : {"Campo 1", "Campo 2", "${field.generated}"}
  }'
}
  • Ejemplo 7: "EEPJ used-based": Evento EEPJ en el que se incorpora un campo usedBy en el que se indicará que es el flujo de creación de los nuevos usuarios 2020. Esto podría ayudar en la gobernanza del evento, a saber que flujo de estados lo creó. Una ampliación a este paso podría ser incorporar el campo step como complemento al campo usedBy en el que se detallaría en qué paso del proceso se produjo.
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  usedby : 'FLOW_USER_2020',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 8: "EEPJ context-based": Evento EEPJ en el que se incorpora un campo context en el que se indicará que es un evento relacionado con el contexto / business, por ejemplo, relacionado con una promoción y no hace una referencia a un evento técnico como puede ser un INSERT en la base de datos.
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL_PROMOTIONAL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  context : 'BUSINESS',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 9: "EEPJ scheme-based": Evento EEPJ en el que se incorpora un campo scheme en el que se indicará cual es el esquema de estructura que sigue el evento en lo relacionado a la forma de representar la información, los atributos utilizados y las restricciones que pudieran existir.
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL_PROMOTIONAL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  scheme : 'EVENT_NEW_USER_V_1_0',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}
  • Ejemplo 10: "EEPJ tracing-based": Evento EEPJ en el que se incorpora un campo traceId en el que se indica el valor de tracking que tiene por ejemplo "1001".
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL_PROMOTIONAL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  traceId : 1001,
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Test content'
  }'
}

1.2. Internacionalización

Tenemos ante nosotros uno de los motivos que ha generado más consumo de ibuprofenos (para el dolor de cabeza) en la historia de la informática empresarial. Para algunos solo es traducir textos, pero para el resto esconde un gran secreto ... NO sólo es traducir, también se incluyen otros elementos como:

  • Formatos de fechas
  • Formatos a la hora de trabajar con tiempo
  • Formatos de los números (enteros, coma flotante, etc.)
  • Formatos de representación de dinero
  • Formatos de medidas (temperatura, capacidad, etc)
  • Formatos sobre los números de teléfono
  • Formatos de dirección
  • Ubicación : país, estado, región, provincia, ciudad, municipio, et..
  • Zonas de tiempo (time zones)
  • Calendarios
  • Código postal
  • ...

¿Ahora ya te duele la cabeza?...jejeje

Seguro que tras leer esto en vuestra memoria ha aparecido alguna batallita en la "derramasteis" alguna lagrimita.

Para poder trabajar de forma correcta con la internacionalización un buen punto de partida podría ser tener en cuenta los siguientes aspectos:

  • La definición del evento siempre debería de estar en el mismo idioma para su total internacionalización.
  • Se aconseja que el idioma propuesto para representar un evento por defecto sea el inglés -> Los atributos de un evento deberían de estar en este idioma, en cambio sus valores puedan estar en otro idioma -> Nada de mezclar idioma en los valores que luego es una fiesta en la que nadie quiere estar...jeje.
  • Se puede utilizar un código para indicar el lenguaje utilizado ([locale] que puede ser lang, languageId, languageCode, ...).
  • Existen varias posibilidades:
  • Implícito: Se presupone que el evento esta en un idioma concreto.
  • Atributo General: El idioma incluido en el propio evento como un atributo.
  • Atributo Específico: El idioma incluido contenido del payload dentro de un evento -> esto permite que el evento este en otro idioma.
  • Se puede facilitar el formato de representación de cualquiera de los elementos de la internacionalización.
  • Otra propuesta es enviar un código del texto a traducir junto con el identificador del idioma con el que se está trabajando, así será el destino el encargado de realizar la traducción.

Ejemplos de uso

Nota: Todos los ejemplos de uso irán centrados en considerar el envío de un email con payload en formato JSON (de aquí en adelante EEPJ (Envío Email Payload JSON)) de esta forma podréis ver las diferencias particulares de cada caso. Tened en cuenta que estas propiedades pueden ser utilizadas en cada caso y que no se exige que sean exactamente como se representan en estos ejemplos.

  • Ejemplo 1: "EEPJ con valores en un idioma concreto implícito": Evento EEPJ que tiene todos los valores en "español" de forma implícito (No se indica)
{
  eventId : 20190701113015,
  eventName : 'ENVIO_EMAIL',
  type : 'CREAR',
  subtype : 'NUEVO',
  object : 'USUARIO',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Contenido Test'
  }'
}
  • Ejemplo 2: "EEPJ con valores en un idioma concreto indicado": Evento EEPJ que tiene todos los valores en "español" de forma implícita en base al atributo, así puede facilitar las traducciones que puedan ser necesarias.
{
  eventId : 20190701113015,
  eventName : 'ENVIO_EMAIL',
  type : 'CREAR',
  subtype : 'NUEVO',
  object : 'USUARIO',
  locale : 'es_ES',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Contenido Test'
  }'
}
  • Ejemplo 3: "EEPJ con un idioma concreto indicado, pero afectando a unos campos concretos": Evento EEPJ que tiene el contenido del email en "español" (sólo afecta al payload).
{
  eventId : 20190701113015,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  locale : 'es_ES',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Contenido Test'
  }'
}
  • Ejemplo 4: "EEPJ con un formato de fecha específico para un caso": Evento EEPJ en el que la fecha de creación viene dada en un formato específico a ese valor.
{
  eventId : 18,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Contenido Test'
  }',
  creationDate : {
    dateformat : 'yyyy-MM-dd',
    date : '2020-12-31' 
  }
}
  • Ejemplo 5: "EEPJ con un formato de fecha específico genérico": Evento EEPJ en el que las fechas vienen dadas en un formato específico para todos los valores.
{
  eventId : 18,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  eventDataFormat  : 'JSON',
  dateformat : 'yyyy-MM-dd HH:mm:ss.SSS',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Contenido Test'
  }',
  creationDate : '2020-12-30 23:15:58.999',
  updateDate : '2020-12-30 23:20:25.153'

}
  • Ejemplo 6: "EEPJ con un formato de fecha específico que se presupone": Evento EEPJ en el que las fechas vienen dadas en un formato aceptado para toda la compañía 'yyyy-MM-dd HH:mm:ss.SSS' donde no hace falta detallarlo dentro del evento.
{
  eventId : 18,
  eventName : 'SEND_EMAIL',
  type : 'CREATE',
  subtype : 'NEW',
  object : 'USER',
  eventDataFormat  : 'JSON',
  payload : '{
      to : 'destino@acme.com',
      from : 'origen@acme.com',
      subject : 'Importante',
      content : 'Contenido Test'
  }',
  creationDate : '2020-12-30 23:15:58.999',
  updateDate : '2020-12-30 23:20:25.153'

}
Información:  Una vez explicado para estos casos os podéis imaginar la cantidad de posibilidad según los aspectos a internacionalizar

1.3. Ordenación

Cómo me podía olvidar de este aspecto, otro de los motivos que generan mayores de dolores de cabeza a los desarrolladores.

Casi siempre existe algún tipo de ordenación. El más básico de todos los mecanismos es establecer el orden creciente de creación de un elemento, es decir, el primero que se crea aparece como primer elemento... y siempre hay primer elemento de algo. Pero la realidad a veces es más complicada y en muchos casos la forma de presentación o la lógica de negocio establece otros tipos de ordenaciones. Por lo tanto, hay que tener en cuenta si será una ordenación ascendente o descendente según un atributo que puede ser: numérico, alfanumérico, fecha, etc.

Y en los casos más complejos la ordenación puede ser "más divertida" al utilizar ordenación compuesta, es decir, se ordena por un campo inicial y si se da el caso que existan duplicados dentro de ese campo se establece una siguiente ordenación dentro de esa consideración y así sucesivamente...vaya lío.

Ejemplo de ordenación de las que hablo
¿Puedes ordenar las facturas del último año ordenadas por mayor facturación a menor en base al color de ojos del perro del cliente?
La realidad puede llegar así de compleja, simplemente bastaría con disponer de la información para ordenar, pero por ejemplo un caso real muy parecido al anterior podría ser: Obtener las ganancias mensuales de las empresas con las que se trabaja en los últimos años con las siguientes consideraciones: agrupación / ordenación descendente por año, ordenación alfanumérica de empresas que han facturado ese año, con ordenación descendente de sus meses según su ganancia (numérico positivo)...
Sí, estoy es lo podéis encontrar habitualmente...jejeje

Como existen mil y una forma de ordenar algo, quedaremos pendientes a los criterios que nos imponga la lógica de negocio considerados.

  • En base a las necesidades de negocio puede ser necesario el establecimiento de algún tipo de ordenación entre eventos:
  • Tiene un carácter opcional a menos que sea un requisito.
  • Pueden necesitar orden en base a cualquiera de los atributos: identificadores, criterios temporales, prioridades, tipología de eventos, alfabético basado en un atributo, numérico, etc.
  • La ordenación suele recaer "normalmente" sobre la pieza que lo vaya a utilizar, así que no suele ser un problema hasta que tienes que indicar el criterio que estas utilizando.
  • Para ello se puede incluir un campo que lo defina.

Ejemplos de uso

Nota: Todos los ejemplos de uso irán centrados en considerar el envío de un email con payload en formato JSON (de aquí en adelante EEPJ (Envío Email Payload JSON)) de esta forma podréis ver las diferencias "particulares" de cada caso. Tener en cuenta que estas propiedades pueden ser utilizadas en cada caso y que no se exige que sean exactamente como se representan en estos ejemplos.

  • Ejemplo 1: "EEPJs ordenación DESCENDENTE por eventId": Eventos EEPJ se ordenan según el campo eventId.
{
  {
    eventId : 25,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-30 23:15:58.999',
    updateDate : '2020-12-30 23:20:25.153'

  },
  {
    eventId : 18,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-29 04:36:58.999',
    updateDate : ''

  }
}
  • Ejemplo 2: "EEPJs ordenación ASCENDENTE por creationDate": Eventos EEPJ se ordenan según el campo creationDate.

Nota : En este caso esta implícito que a mayor fecha de creación mayor debería ser el id de creación, pero otros criterios de ordenación puede que esto no pase.

{
  {
    eventId : 18,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-29 04:36:58.999',
    updateDate : ''

  }, 
  {
    eventId : 25,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-30 23:15:58.999',
    updateDate : '2020-12-30 23:20:25.153'

  }
}
  • Ejemplo 3: "EEPJs ordenación DESCENDENTE por prioridad": Eventos EEPJ se ordenan según el campo priority.
{
  {
    eventId : 18,
    priority : 10,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-29 04:36:58.999',
    updateDate : ''

  }, 
  {
    eventId : 25,
    priority : 5,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-30 23:15:58.999',
    updateDate : '2020-12-30 23:20:25.153'

  }
}
  • Ejemplo 4: "EEPJs ordenación principal DESCENDENTE por prioridad y en caso de igualdad en la misma prioridad DESCENDENTE por eventID ": Eventos EEPJ se ordenan según el campo priority y eventId.
{
  {
    eventId : 18,
    priority : 10,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-29 04:36:58.999',
    updateDate : ''

  }, 
  {
    eventId : 25,
    priority : 5,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-30 23:15:58.999',
    updateDate : '2020-12-30 23:20:25.153'

  },
  {
    eventId : 43,
    priority : 5,
    eventName : 'SEND_EMAIL',
    type : 'CREATE',
    subtype : 'NEW',
    object : 'USER',
    eventDataFormat  : 'JSON',
    payload : '{
        to : 'destino@acme.com',
        from : 'origen@acme.com',
        subject : 'Importante',
        content : 'Contenido Test'
    }',
    creationDate : '2020-12-31 01:04:33.587',
    updateDate : ''

  }
}

2. Ampliación de Actores implicados en un Evento

Ampliamos los actores explicados en el primer artículo:

  • Evento (Evento)
  • Productor / Emisor / Publicador de Eventos (Event Publisher)
  • Canal (Channel)
  • Consumidor/Receptor de Eventos (Event Consumer)

Para ello definiré algunas funcionalidades que puede utilizarse sobre los anteriores actores:

  • Propagador (Propagator): Componente SW que suele ser una mezcla entre consumidor y productor. Se encarga de procesar un determinado evento, realiza una serie de acciones sobre él y lo vuelve emitir (generando otro evento o bien propagando el mismo evento con o sin modificación).
  • Planificador (Planner): Componente SW que es responsable de controlar los arranques y paradas de las diferentes piezas (productores y consumidores) de acuerdo a criterios de temporalidad (número de minutos, segundos, etc) o bien de calendario (cada Lunes, sólo el primer Jueves del mes, una fecha específica, ...).
  • Expirador (Expirer): Componente SW que es responsable de "caducar" los eventos que NO son válidos respecto alguno o varios criterios (expiration time, creation Date). Esta parte se puede implementar como una pieza que ataque al canal directamente o que sea el propio consumidor el que caduca el evento a la hora de procesarlo.
  • Transformador (Transformer): Componente SW que es responsable de convertir la información entre diferentes formatos, cambiar la estructura, añadir/actualizar/quitar información, añadir meta información, etc. Este componente suele estar relacionado con los propagadores.
  • Replicador (Replicator): Componente SW que es responsable de generar copias de la información para: backups, distribución como difusión, interacción con otros sistemas, etc.
  • Limpiador (Cleaner): Componente SW que es responsable de eliminar los eventos que no son válidos en un determinado flujo de trabajo (por ejemplo un evento de otro tipo en una cola).
  • Filtrador (Filter): Componente SW que es responsable de localizar aquellos elementos que cumplen algún tipo de criterio establecido con el objetivo de realizar alguna acción sobre ellos: separar por validez, determinar el cumplimiento de alguna prioridad, eliminarlos, etc . Este componente suele estar relacionado con los propagadores. Hay que tener en cuenta que se puede trabajar con un elemento específico o bien con un conjunto y, por otro lado, hay que definir que pasa con los casos que cumplen con el criterio y lo que pasa con lo que no la cumplen.
  • Validador (Validator): Componente SW que es responsable de comprobar la corrección de la estructura y/o el contenido de un evento basado en la lógica de negocio. Por lo tanto, se facilita la detección de eventos inválidos, información incorrecta, estados incorrectos dentro de un flujo, facilita los filtrados, etc.
  • Enrutador (Router): Componente SW que es responsable de redirigir la información a uno o varios puntos (aplicaciones, plataformas, servidores o componentes de generación de eventos) en base a uno o varios criterios establecidos (por ejemplo mediante el uso de criterios de clasificación).
  • Secuenciador (Sequencer): Componente SW que es responsable de establecer un flujo de trabajo entre diferentes puntos y/o funcionalidades de los componentes. Para ello, se debería determinar qué se necesita en cada step para avanzar al siguiente hasta que este se considere completo.
  • Encriptador/Desencriptador (Encryptor/Decryptor): Componente SW que es responsable de encriptar o desencriptar la información en base a los requerimientos de seguridad.
  • Firmador Digital (Digital signatory): Componente SW que es responsable de firmar digitalmente el evento o bien alguno/os de sus campos.
  • Priorizador (Prioritizer): Componente SW que es responsable de establecer algún criterio de priorización de los eventos en base a alguno de sus campos.
  • Invocador (Invoker) : Componente SW que es responsable de establecer la conversión entre un evento a otro formato posiblemente debido a tener que llamar a otro sistema (por ejemplo: REST, base de datos, etc.)
  • ...

Os acabo de pasar una lista de muchas de los componentes o "actores" con los que me he tenido que encontrar, pero os puedo garantizar que existen muchos más...jejeje

3. Conclusiones

Este artículo y su siguiente (Parte3) están enfocado en tratar ciertos temas complejos y algunos de los aspectos más típicos de las problemáticas planteadas por las empresas actuales, lo que pasa es que esta problemática puede ser muy amplía según cada caso...jeje. Quizás dependiendo de vuestro "destino" no necesitéis nunca tener que implementarlos, quizás os toque hacerlo siempre o sólo algunas veces... ya se verá.

Se ha decidido mantener la estrategia del primer artículo a la hora de presentar los conceptos con ejemplos: siempre ayudan a entender mejor las cosas y así sirven de base para definir vuestros casos particulares.

Resumen de los puntos a tratar:

  • Organizativas
  • Internacionalización
  • Ordenación
  • Inmutabilidad
  • Expiración
  • Ubicación
  • Seguridad
  • Modos de Ejecución

En concreto, en ese artículo se han abordado los temas en negrita. En la siguiente parte (Parte 3) se presentarán otros restantes y pueden ser tan "divertidos" como estos. Además, se ha enseñado como extra algunos nuevos componentes lógicos ("actores") que pueden ser utilizados en este tipo de arquitecturas y que pueden ayudar a definir que puede ser necesario implementar.

Espero que os haya servido para aclarar algunas dudas.

Así que permanece atento porque en breve cerraremos esta serie de artículos ;-)

Si te ha gustado, ¡síguenos en Twitter para estar al día de nuevas entregas!

Autor

Víctor Madrid

Líder Técnico de la Comunidad de Arquitectura de Soluciones en atSistemas. Aprendiz de mucho y maestro de nada. Técnico, artista y polifacético a partes iguales ;-)