Este post forma parte de una serie de artículos sobre el diseño de soluciones de arquitectura.

En el artículo anterior repasamos los aspectos del contexto de un proyecto que deberíamos tener en cuenta para orientar la solución lo mejor posible y planteamos un ejercicio de ejemplo para ponerlo en práctica.

Una vez entendido mejor el contexto y los requisitos, comenzamos a diseñar la solución de arquitectura. En este artículo nos centraremos en la arquitectura funcional, que describe la solución lógica desde un punto de vista de negocio y la funcionalidad requerida.

Casos de uso

Para comenzar el diseño de la arquitectura necesitamos identificar los casos de uso que definen el funcionamiento deseado en los requisitos. Estos casos de uso nos dan el alcance de la solución y serán el esqueleto sobre el que diseñemos la arquitectura.

Si los requisitos están ya definidos mediante casos de uso, o disponemos de un análisis funcional orientado por casos de uso, podemos partir del análisis ya realizado y seleccionar aquellos que tienen impacto en la arquitectura. Si no, tendremos que identificarlos como parte del análisis para el diseño de arquitectura, apoyados por el equipo de desarrollo y negocio, ayudándolos a estructurar el alcance del proyecto y a validar los requisitos.

Los casos de uso que tienen impacto en la arquitectura son los que plantean cambios estructurales en los procesos de negocio, las aplicaciones, las integraciones, los datos, las tecnologías o infraestructura. En algunos casos será más evidente y en otros, no tanto. Si tenemos dudas sobre si un caso de uso tendrá impacto en la arquitectura, lo incluimos y más adelante veremos si realmente es necesario continuar detallándolo.

Para identificar los casos de uso podemos utilizar diversas técnicas:

Para describir los casos de uso, la granularidad del detalle de los casos de uso de arquitectura será menor que la de los casos de uso de aplicación. Desde el punto de vista de arquitectura, el planteamiento es similar al de UML pero sin entrar en el detalle de la lógica interna de las aplicaciones. Se trata más de estructurar los procesos y la secuencia de pasos entre usuarios y aplicaciones, dejando las aplicaciones como cajas negras.

Si los modelamos como subprocesos con BPMN, habría que considerar a las aplicaciones como actores e incluir las interacciones lógicas entre aplicaciones, no solo desde la perspectiva de los usuarios.

En cualquier caso, no es tan importante el lenguaje formal como tener claros los elementos de detalle que necesitamos modelar. En el post sobre anatomía de diseño de soluciones de arquitectura, planteo un framework para definir y personalizar las vistas de arquitectura a las necesidades particulares de nuestro contexto. Para modelar los casos de uso podemos utilizar UML, BPMN, DFD o cualquier otro lenguaje con el que podamos describir los casos de uso como más nos interese y nos sintamos más cómodos.

Si tenemos varios casos de uso que son similares desde un punto de vista de arquitectura, y tiene sentido funcional hacerlo, los podemos agrupar en un solo caso de uso para describir la arquitectura de forma más sencilla, omitiendo detalles que no sean relevantes desde nuestro punto de vista.

Por ejemplo, podemos tener casos de uso de operaciones CRUD sobre un mismo servicio que solo se diferencian en la lógica interna. Si es así, podemos plantear un solo caso de uso de gestión de esa entidad que represente a todas las operaciones para hacer el diseño de forma más eficiente y legible. Eso sí, tengamos siempre cuidado de validar que efectivamente los requisitos no necesitan diferencias entre los casos de uso.

Caso práctico

Recuperemos el proyecto que estamos analizando en esta serie de artículos para entenderlo mejor. En este caso voy a utilizar UML como notación de modelado. Los casos de uso que identifico para el problema son los siguientes:

Caso de uso
Caso de uso

Probablemente no llegaremos a este nivel de detalle o estructuración de casos de uso en la primera aproximación. Lo importante es hacer un primer planteamiento sobre el que iterar con los equipos de negocio y desarrollo.

Tenemos varios escenarios en los que necesitamos ejecutar las operaciones de forma automática de acuerdo a las necesidades de planificación de los turnos en fechas fijas cada mes. En cada hito del calendario se envían notificaciones a los usuarios para recordarles que deben registrar sus preferencias de guardias en los plazos establecidos. El hito de cierre del período será el encargado de calcular la planificación del siguiente mes.

Podemos observar que el caso de uso de envío de notificación es común a varios casos de uso, aunque el contenido de las notificaciones sea diferente. He agrupado el envío de notificaciones de cada casuística en un solo caso de uso, ya que su arquitectura se podría plantear similar, e incluirlo como parte de ellos. Es posible que en una primera iteración no tengamos claro si su arquitectura es similar en cada caso. Planteando el diseño como un proceso iterativo, esta simplificación podemos realizarla una vez que hayamos profundizado el resto del modelo y comprobemos que efectivamente podemos hacerla.

El resto de casos de uso serán funcionalidades gestionadas por los sanitarios y el gestor del hospital en la aplicación.

Una vez identificados los casos de uso, podemos analizar los requisitos no funcionales asociados a los casos de uso.

Se puede pensar que los requisitos no funcionales son técnicos y deben analizarse sobre la arquitectura técnica pero, en mi opinión, los requisitos no funcionales deberían definirse desde un punto de vista de negocio porque es quien tiene la capacidad de cuantificar su impacto.

Para mí, la clave está en asociarlos a los casos de uso de forma que podamos pedirlos o plantearlos en términos de negocio, en un lenguaje cercano y manejable para los usuarios. No tiene sentido preguntarles sobre el número de peticiones que debe soportar un servicio, pero sí que podemos preguntarles sobre el número de operaciones que esperan que se realicen en un caso de uso, el crecimiento de operaciones o el volumen de usuarios que esperan tener.

A partir de los requisitos no funcionales a nivel de caso de uso, podemos transformar y trasladar estos valores a los componentes técnicos de una manera prácticamente automática. Lo veremos sobre el ejemplo.

Para facilitar el análisis de los requisitos no funcionales de los casos de uso, propongo estas tablas. IDCU sería el ID del caso de uso que permite trazar todos los datos.

Volumetrías

IDCU Ejecuciones diarias promedio Ejecuciones en momento pico Crecimiento esperado en 1 año (%)
< ID> < Ejecuciones promedio diarias> < Número máximo de ejecuciones en un momento dado> < Expectativa de crecimiento en la actividad de negocio relacionada>

Requisitos no funcionales

IDCU Planificación Horario de actividad
< ID> < Online, diario...> < Horario máximo de ejecución del caso de uso. Pe: 24x7,8x5>
RTO MTPD RPO
< Cuánto tiempo puede estar el proceso sin funcionar sin afectar al negocio económicamente o reputacionalmente> < Cuál es el tiempo máximo tolerable, en el que se producen pérdidas inaceptables o irrecuperables> < Tiempo de datos que se puede perder sin afectar al negocio económicamente o reputacionalmente>
Tiempo de respuesta / latencia Localización Plan de contingencia
< Tiempo límite que puede durar el proceso sin afectar al negocio> < Localización geográfica del usuario o del dispositivo> < Acciones que se pueden tomar para que el negocio siga funcionando en caso de que el proceso falle de forma persistente>

Los requisitos no funcionales son vitales para que el diseño de la solución de arquitectura cumpla adecuadamente con los objetivos de negocio y su definición afecta de forma muy importante al dimensionamiento, redundancia y coste de los sistemas, tanto de mantenimiento como de infraestructura.

Si preguntamos directamente por los requisitos no funcionales al negocio, lo normal es que siempre pidan las mejores prestaciones, sean realmente necesarias o no. Para evitar requisitos no funcionales caprichosos es importante, al establecerlos, que el negocio explique qué impacto tendría no cumplirlos y buscar la configuración mínima que permita implementar la arquitectura sin afectación o con poco riesgo para el negocio.

Para orientar el punto en el que el impacto por mal funcionamiento es alto, podemos crear categorías de impacto que cuantifiquen con los efectos con valores o rangos específicos y evaluar, para los posibles tiempos de mal funcionamiento, qué categoría aplica según el valor de la pérdida.

Tabla de valor de la pérdida
Tabla de proceso, función o servicio

Podemos establecer el RTO el tiempo de parada en el que comienzan a haber impactos altos, y el MTPD cuando empiezan a ser críticos.

Igualmente, podemos aplicar estos criterios al RPO con los impactos asociados a las pérdidas de datos.

Caso práctico

Tras algunas entrevistas y correos con los usuarios y tenemos la siguiente información de volumetrías:

IDCU Ejecuciones promedio Ejecuciones en momento pico Crecimiento esperado en 1 año (%)
INIQ1 1 / mes 1 / mes 0%
INI02 1 / mes 1/ mes 0%
CIE01 1 / mes 1 / mes 0%
INI01.01 4/ mes 4 / mes 0%
CIE01.02 1 / mes 1 / mes 0%
SEL01 700 / mes 200 / día 0%
REV01 2/ mes 4 / día 0%
REV01.01 2/ mes 4 / día 0%
REV01.02 2 / mes 4 / día 0%
REV01.02.01 1.601 / mes 8.001 / día 0%
CAM01 800 / mes 4000 / día 0%
CAMO2 400 / mes 2000 / día 0%
САМОЗ 400 / mes 2000 / día 0%
CAMO4 400 / mes 2000 / día 0%
CAM05 10/ mes 20 / día 0%

La gestión se realiza en ciclos mensuales y el volumen de operaciones es bastante bajo incluso en los momentos pico. Ya podemos intuir que el sistema a implementar no requiere grandes recursos para gestionar las peticiones de usuario o procesos automáticos, aunque hay otros requisitos no funcionales a tener en cuenta para poder confirmarlo, que veremos más adelante.

También podemos ver que no hay previsión de crecimiento de carga para el próximo año, ya que el tamaño del equipo de usuarios, que es la principal variable de la carga, es fijo.

Veamos los otros requisitos no funcionales:

Requisitos no funcionales

Podemos ver que los casos de uso permiten unos tiempos de recuperación gestionables manualmente, por lo que en caso de incidencia no se plantea ningún plan de contingencia automatizado.

Los procesos batch tienen cierto margen de tiempo para su ejecución, pero los casos de uso manuales deberían tener una experiencia fluida para los usuarios.

Funcionamiento de los casos de uso

Ahora llega el momento de describir el funcionamiento dinámico de los casos de uso. Para hacerlo, podemos utilizar diagramas de secuencia o de colaboración de UML o, si queremos un modelo más estructural, podemos utilizar lenguajes como Archimate.

Para realizar estos modelos debemos identificar para cada caso de uso qué usuarios y aplicaciones deben interactuar, describiendo los pasos a seguir ordenadamente para conseguir el resultado deseado en el caso de uso de la forma más simple y eficiente posible.

A la hora de elegir los usuarios y aplicaciones que realizan cada paso del caso de uso, debemos pensar en la responsabilidad funcional que tiene cada uno de ellos en el conjunto de la organización o el mapa de aplicaciones. Aquí es donde debemos aplicar criterios estratégicos claros para la asignación de funciones.

Por ejemplo, para poder identificar qué usuarios o roles son los que deben realizar determinados tipos de tareas manuales, deberíamos plantear si el reparto de funciones en la organización se hace mediante departamentos especializados en los tipos de tareas a realizar o por equipos multidisciplinares organizados por funciones de negocio.

Por otro lado, podríamos ordenar las funcionalidades en las aplicaciones agrupando, mediante dominios funcionales o dominios de datos, por grupos de procesos de negocio, por grupos de usuarios o por etapas de la cadena de valor del negocio, por mencionar algunos criterios.

Estos criterios no deberían establecerse en el propio diseño de la solución, sino que deberían estar ya definidos como criterios estratégicos. Si no fuese así, deberíamos elevar el riesgo para discutirlo en foros más estratégicos, ya que estos criterios deberían ser globales y aplicarse de forma generalizada para conseguir una arquitectura coherente y cohesionada.

Otro aspecto en el que poner foco es en las interacciones. Los usuarios y aplicaciones necesitan comunicarse para coordinarse en la ejecución de los pasos ordenadamente. Para estas comunicaciones, debemos ir tomando decisiones sobre qué datos necesitan comunicar y si la comunicación debe ser síncrona o asíncrona. Incluso, a nivel funcional, podemos determinar si utilizaremos paradigmas de mensajería o eventos para comunicaciones asíncronas, considerando ya cuestiones como la modularidad o el acoplamiento de los componentes.

Es importante la identificación de los datos clave necesarios para que el caso de uso pueda funcionar correctamente. No hay que entrar en el detalle de todos los campos ya que el objetivo no es diseñar el modelo de datos o la interfaz, pero sí se necesitan identificar las entidades y atributos clave imprescindibles para que cada componente pueda realizar su función. Hay que validar en este punto que los datos necesarios realmente existen, tienen el formato, contenido y calidad adecuados, y podrán obtenerse según se indica en el diseño.

Otro aspecto que podemos revisar en los casos de uso es la necesidad de transaccionalidad en las operaciones. Esto permitirá separar la lógica transaccional y la no transaccional en componentes diferentes y revisar el orden de ejecución de los pasos del caso de uso para minimizar la complejidad de las transacciones.

Cuando pensemos en los casos de uso, deberíamos ir más allá del “happy path” y analizar las casuísticas o variantes que podrían afectar al diseño de la arquitectura, incluyendo la gestión de los posibles errores funcionales en las interacciones.

Es importante revisar e iterar lo necesario en cada caso de uso para que sea lo más completo, coherente y eficiente posible pero, si nos falta información o puntos abiertos por resolver, no nos atasquemos. Podemos hacer las asunciones que necesitemos sobre cada punto abierto para poder continuar y gestionar mientras tanto la resolución de estos puntos.

El diseño del comportamiento dinámico de los casos de uso es el momento para aplicar la creatividad y diseñar la solución, y debería ser el foco de discusión con la mayoría de las partes interesadas, por lo que es importante ponerle atención y cariño aunque pueda ser la parte más laboriosa.

Estos modelos son clave para revisar con los equipos de negocio y desarrollo y definir cómo queremos que funcione la arquitectura, ya que dispondremos de un soporte visual que nos permite explicar y discutir con facilidad los puntos abiertos y decisiones a tomar, con una granularidad de detalle suficiente.

Caso práctico

Veamos cómo funcionarán algunos de los casos de uso en nuestra solución:

Caso de uso

Este caso de uso representa los pasos a dar para realizar el envío de la notificación de inicio del período de priorización de guardias a los usuarios.

Para realizarlo se ha identificado que hace falta una aplicación que tenga la función de planificación de los procesos que se realizan de forma periódica. Esta es una función que es común a cualquier dominio funcional, por lo que la decisión es que se implemente como una aplicación común que no pertenece a ninguno de los dominios funcionales.

El planificador lanza un evento cada día 15 del mes. El servicio de inicio del período de priorización de guardias gestiona este evento y determina que debe enviar una notificación a los usuarios. En el análisis del contexto, identificamos que ya existe la aplicación de servicios de RRHH para gestionar este tipo de servicios, por lo que decidimos implementarlo en esta aplicación.

Al analizar los casos de uso, vemos que el envío de la notificación es una funcionalidad común de varios casos de uso y que puede ser reutilizable también por otras aplicaciones y procesos, por lo que se decide separar el envío en un servicio diferente en una nueva aplicación de gestión de notificaciones. El servicio de inicio del período tendrá que pasar al servicio de envío de notificaciones los datos necesarios como el contenido a enviar, los destinatarios o el canal de envío de la notificación.

Para facilitar la gestión de notificaciones, se plantea que el servicio de envío utilice plantillas configurables por los administradores, pero para desacoplar esta necesidad técnica de los servicios funcionales que necesitan enviar notificaciones, se crea una abstracción de tipo de notificación, que permite indicar qué notificación se quiere enviar sin detalles técnicos. El gestor de notificaciones tendrá ya configurado para cada tipo de notificación qué plantilla aplicar.

Finalmente, el servicio de envío de notificaciones crea los emails con los datos y destinatarios correspondientes y los envía.

Los casos de uso INI02 y CIE01 son similares desde el punto de vista funcional. No los detallo en el artículo por brevedad en la lectura. Podemos intuir que el cálculo de la planificación será un servicio pesado, ya que implementa toda la lógica de cálculo de la planificación a partir de la información existente. Vamos a poner foco en este subcaso de uso:

Subcaso de uso

Para calcular la planificación, lanzaremos el proceso cuando se produzca el evento Período de priorización de guardias cerrado generado por el servicio de Cierre del período de guardias.

El objetivo de la arquitectura no es diseñar la lógica interna del proceso. Probablemente, se usaría algún tipo de algoritmo de sistema experto basado en reglas o algoritmo de IA, pero no es relevante para la arquitectura desde el punto de vista funcional.

Lo que sí necesitamos es identificar las dependencias de datos de entrada y salida del proceso. Este proceso necesitará, al menos, disponer de la información contractual asociada a las condiciones laborales de horarios del equipo sanitario, del calendario laboral asociado al mes a planificar y las preferencias de guardias registradas para intentar priorizarlas.

En el anterior artículo obtuvimos del contexto que la información de empleados y calendario laboral se gestionan en la aplicación de Gestión de empleados, que proporciona APIs de consulta de esta información, por lo que podremos utilizarlas para este diseño. Adicionalmente, deberíamos validar con el equipo de la aplicación y el equipo de desarrollo si los datos a consultar son los que necesitamos o necesitan algún tipo de complemento o transformación. En este caso no es necesario.

Las preferencias de guardias se gestionan en la propia aplicación de Servicios de RRHH, por lo que el servicio podrá consultarlas.

Finalmente, cada vez que concluye el cálculo de planificación, invocará al servicio de Guardar planificación. Realmente no es necesario separarlo en un servicio adicional, pero necesitamos expresarlo de alguna manera en el modelo y lo representamos como un servicio compuesto que forma parte del servicio de cálculo de la planificación.

Veamos otro ejemplo y las decisiones clave que podemos tomar ya:

Caso de uso

Este es el caso de uso REV01 para revisar la planificación. El gestor necesita de una funcionalidad para revisar la planificación en una aplicación, que será también la app de servicios de RRHH por el mismo criterio que en los anteriores casos de uso.

Tenemos información suficiente para determinar que esta funcionalidad necesitará dos servicios para consultar y modificar la planificación. Por reutilización y simplicidad, se decide que la funcionalidad trabaje sobre la planificación de cada persona unitariamente, por lo que debe diseñarse para iterar sobre cada planificación que modifique el gestor y los servicios actuarán cada vez sobre una planificación. Si tuviésemos grandes volúmenes de datos que procesar en cada acción, quizá podríamos optar por trasladar esta iteración a los servicios para ser más eficientes en el número de llamadas a servicios, pero en este caso las modificaciones deberían ser puntuales y podemos optar por este enfoque más modular.

Para el envío de la notificación se usa comunicación mediante eventos, similar al caso de uso descrito anteriormente, permitiendo desacoplar el envío de la notificación en otro servicio independiente de la operación transaccional de modificación de la planificación. La modificación de una planificación genera un evento indicando que se ha realizado un cambio, y el servicio “Enviar notificación a sanitario afectado” escucha este evento para enviar la notificación correspondiente a la persona afectada por el cambio, utilizando el mismo servicio de envío de notificaciones anterior.

Se podría plantear que el servicio de envío de notificaciones se suscriba directamente al evento, pero eso crearía un acoplamiento funcional con la interfaz del evento. Para que este servicio sea reutilizable, debe proporcionar una interfaz propia estándar y utilizar el servicio intermedio como intérprete del evento específico del dominio y como responsable de la lógica a implementar como reacción al evento.

A nivel de datos, en nuestro caso hemos verificado o indicado que los datos de planificación y sanitarios están en la propia aplicación de Servicios de RRHH.

El resto de casos de uso pendientes de detallar serán parecidos y más simples. En un modelo de solución completo realizaríamos el diseño de todos los casos de uso, pero en este artículo los recortamos para facilitar la lectura.

Modelo funcional

Con el desarrollo de cada uno de los casos de uso, tenemos ya información descriptiva suficiente de la arquitectura a nivel de detalle. Pero, cuando las soluciones son complejas, necesitamos también valorar el resultado con perspectiva.
Para hacerlo, agrupamos el diseño de todos los casos de uso en un solo diagrama que nos permitirá ver cómo queda la solución en un vistazo.

Este punto de vista permite visualizar la estructura general de la arquitectura e identificar elementos repetidos, dependencias inconsistentes, incoherencias en la solución de los casos de uso individuales, cuellos de botella, alternativas más simples… Además, permite validar fácilmente cómo estamos aplicando los patrones de diseño y paradigmas de arquitectura.

Este diagrama será de más alto nivel, poniendo foco en las aplicaciones y dependencias entre aplicaciones. Los componentes y sus integraciones se agruparán a nivel de aplicaciones. Como contrapartida, perdemos el detalle de orden de ejecución de los casos de uso y de los componentes o módulos que realizan las integraciones, pero para eso ya tenemos los modelos de secuencia de los casos de uso.

El contenido de esta vista sería el siguiente:

Caso práctico

Nuestro ejemplo podría quedar de la siguiente manera:

Modelo funcional

Para el resto de casos de uso, hemos indicado el resto de eventos planificados en el planificador. Hemos añadido el resto de funcionalidades de gestión de cambios de turnos con los servicios y eventos necesarios para su funcionamiento. No aparecen nuevos datos relevantes, aunque será necesario gestionar el estado de las solicitudes de cambio de turno. El servicio de envío de notificaciones se reutiliza para el resto de notificaciones.

En un simple vistazo, podemos ver qué aplicaciones están afectadas o hay que desarrollar, qué dependencias hay con otros equipos, si es necesario identificar nuevos responsables de aplicaciones, o hacernos una idea de la complejidad o coste del proyecto.

Podemos utilizar un código de colores para ver más claramente los impactos:

Modelo funcional con código de colores

Cuando tenemos definida la arquitectura funcional por primera vez, es un buen momento para una revisión de la solución con el equipo de especialistas (en este caso, de negocio) y las personas interesadas antes de proceder con el diseño técnico. En este artículo describimos la metodología de diseño.

Esto no quiere decir que la arquitectura funcional deba estar completa antes de avanzar con el resto del diseño, sino que validamos lo que tengamos para que, en la siguiente iteración, revisemos el feedback de la arquitectura funcional y ya profundicemos en el diseño técnico de los casos de uso que tengamos más maduros.

Resumen de los pasos para desarrollar la arquitectura funcional: analizar la información, obtener las dudas, resolver las dudas con referentes, diseñar o revisar el modelo de solución, revisar el diseño con especialistas, revisar el modelo con interesados, analizar feedback, cerrar versión del modelo de solución

Conclusión

En este artículo hemos visto cómo identificar y detallar los casos de uso a implementar en el proyecto a partir de la información del contexto y los requisitos de negocio.

Una vez desarrollados los casos de uso, es sencillo crear un modelo funcional de arquitectura que permita tener una visión clara del proyecto.

En el próximo artículo veremos cómo aterrizar la solución funcional en una solución técnica.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete