Un análisis profundo de los estilos y el particionamiento de estructura en la arquitectura de software

Aprende sobre los estilos de arquitectura de software y el particionamiento de estructura, su implementación, casos de uso y un ejemplo práctico de un sistema TODO.

Un análisis profundo de los estilos y el particionamiento de estructura en la arquitectura de software

A lo largo de mi carrera como desarrollador de software, mientras trabajaba para una gran empresa de consultoría de software, tuve la oportunidad de participar activamente en múltiples proyectos con diferentes clientes. Una de las experiencias más fructíferas fue cuando tuve que lidiar con sistemas heredados (legacy) y con sistemas nuevos (green field) que se implementaron con arquitecturas monolíticas, en capas, orientadas a servicios, dirigidas por eventos, de microservicios y de microkernel. Tales experiencias me proporcionaron un mecanismo para decidir qué tipo de estilo arquitectónico y particionamiento de estructura debían implementarse en función del alcance del proyecto: sus necesidades de negocio y los requisitos del cliente.

En esta publicación voy a compartir qué son el estilo de arquitectura de software y el particionamiento de estructura, cuándo deben implementarse, casos de uso y un sistema TODO de ejemplo donde pondremos en práctica todo lo explicado.

Arquitecturas de software

Componentes de la arquitectura de software

La empresa de consultoría donde trabajaba estipulaba dos prácticas o actividades principales que debían seguirse al iniciar un proyecto con un cliente, que eran: discovery e inception. Esas actividades involucraban la participación de múltiples miembros del equipo con diferentes roles como BA, XD, PM, QA, Dev e ingenieros de infraestructura, con el objetivo de mapear todos los requisitos y perspectivas desde el lado del negocio y del lado técnico.

En cada proyecto que requiere servicios de consultoría, normalmente comenzamos con la fase de discovery, cuyo objetivo es que el equipo del proyecto reúna y evalúe información sobre los requisitos del proyecto, los stakeholders y las restricciones. Lo siguiente era ejecutar la fase de inception, donde nos enfocábamos en definir el alcance del proyecto, normalmente con un producto mínimo viable (MVP), establecer un plan de proyecto de alto nivel y diseñar la arquitectura de software inicial.

Durante la fase de inception, el equipo técnico estaba más activamente involucrado, ya que uno de los resultados clave era el diseño de la arquitectura: los componentes de software fundamentales y las interacciones dentro de un sistema de software a alto nivel, que sirven como un plano para su desarrollo. Este proceso estratégico de toma de decisiones implica la consideración de los componentes de la arquitectura y guía al equipo de desarrollo en la implementación de requisitos funcionales y no funcionales que permiten a las organizaciones adaptarse a necesidades de negocio en constante evolución.

En ese momento, yo usaba el término estructura de arquitectura de software para proporcionar una arquitectura de alto nivel a los clientes y a los miembros del equipo; luego, durante la fase de desarrollo, usaba el término patrones de arquitectura de software para implementar el componente de software. Después de leer el libro de Mark Richards: Software Architecture Patterns, encontré que hay una mejor manera de diferenciar esas fases relacionadas con el diseño y la implementación de la arquitectura, que son: estilo de arquitectura y particionamiento de estructura de arquitectura. Esos términos se usarán en los siguientes apartados de esta publicación para describir los pasos a seguir en el proceso de diseño e implementación de la arquitectura de software.

Arquitectura de software: fases de estilo y particionamiento de estructura

Estilos de arquitectura

Estilos de arquitectura

Un estilo de arquitectura de software encarna un conjunto de principios de diseño, directrices y buenas prácticas para organizar y estructurar los componentes de un sistema de software a alto nivel. Los estilos proporcionan un enfoque consistente para resolver problemas comunes de diseño de software que a menudo encapsulan soluciones probadas, facilitando a las personas desarrolladoras la creación de sistemas mantenibles y escalables.

En el campo del desarrollo de software, es un hecho bien reconocido que a menudo existen múltiples soluciones para un problema o caso de uso dado. Esta amplia diversidad de enfoques para resolver problemas ha llevado al uso popular de la frase “depende” dentro de la industria. La frase es popular en las empresas de consultoría, ya que resalta la importancia de considerar diferentes factores, como los requisitos, las restricciones y el contexto, al determinar la solución más adecuada para un reto de software específico.

Para seleccionar el estilo de arquitectura de software adecuado para el proyecto, debemos alinearlo tanto con las necesidades del negocio (metas y objetivos de la organización) como con los requisitos del cliente (requisitos funcionales y no funcionales específicos). Además, realizar un análisis de trade-off (compensaciones) puede explicar objetivamente por qué una opción específica es el estilo arquitectónico más adecuado para el proyecto. Esta fase del proceso de diseño de la arquitectura de software tiene una importancia significativa, ya que la fase de implementación posterior se verá considerablemente afectada por las decisiones tomadas en esta etapa, por lo que una planificación y un análisis cuidadosos son esenciales para el éxito de un proyecto.

Algunos ejemplos de estilos arquitectónicos comunes son:

  1. En capas (Layered)
  2. Arquitectura orientada a servicios (SOA)
  3. Monolito modular (Modular Monolith)
  4. Microkernel
  5. Arquitectura dirigida por eventos (EDA)
  6. Microservicios

Particionamiento de estructura de arquitectura

Particionamiento de estructura de arquitectura

Una vez que hemos elegido un estilo de arquitectura, podemos pasar al particionamiento de estructura de arquitectura de software. Este proceso ayuda a crear un sistema modular, mantenible y organizado que se alinea con el estilo arquitectónico elegido. La elección de la estrategia de particionamiento depende de factores como el tamaño del sistema, la complejidad y los requisitos específicos de la arquitectura.

El particionamiento de estructura de arquitectura está relacionado con la fase de desarrollo o implementación del proyecto, con el objetivo de entregar lo que se acordó como un MVP. En los siguientes apartados vamos a cubrir los aspectos de implementación del particionamiento de estructura de software.

Implementación del particionamiento de estructura de arquitectura de software

En la ingeniería de software, desarrollar un sistema bien estructurado y mantenible es crucial para el éxito a largo plazo. Uno de los enfoques clave para lograrlo es a través del particionamiento de estructura de arquitectura de software. El proceso de implementación para el particionamiento de estructura implica dividir un sistema en unidades más pequeñas y manejables, lo que mejora la modularidad y la mantenibilidad. Se puede abordar de dos maneras principales: técnica y de dominio.

Particionamiento de estructura técnico y de dominio

Particionamiento de estructura: Técnico

Este enfoque se centra en organizar el sistema en unidades distintas según sus responsabilidades técnicas, lo que puede promover la separación de responsabilidades y reducir la complejidad. Ejemplos de estrategias de particionamiento técnico incluyen:

  1. En capas (Layered): Organizar el sistema en capas horizontales distintas, donde cada capa proporciona servicios a la capa superior y depende de los servicios de la capa inferior. Las capas comunes incluyen presentación, negocio y persistencia.
  2. Arquitectura orientada a servicios (SOA): Descomponer el sistema en servicios independientes que se comunican entre sí mediante interfaces o APIs bien definidas.
  3. Arquitectura dirigida por eventos (EDA): Los componentes se organizan en torno a la comunicación asíncrona de eventos. Los productores de eventos generan eventos, mientras que los consumidores de eventos los procesan. El bus de eventos o broker de mensajes sirve como la columna vertebral de comunicación, conectando a productores y consumidores mientras mantiene su desacoplamiento.
  4. Microkernel: También conocida como la arquitectura de plugins, organiza los componentes en un sistema núcleo (microkernel) y un conjunto de plugins o extensiones. El microkernel es responsable de proporcionar la funcionalidad esencial del sistema y de gestionar la comunicación entre los plugins, mientras que los plugins implementan funcionalidades específicas, lógica de negocio o funcionalidad de dominio. Esta es una arquitectura particular, ya que podría ser un particionamiento de estructura técnico o de dominio, dependerá de cómo se usen los plugins. En el caso del particionamiento de estructura técnico, los componentes de los plugins están diseñados para abordar aspectos o preocupaciones técnicas específicas. Por ejemplo, si los plugins representan diferentes módulos en una plataforma de analítica, como conectores de datos, transformaciones de datos y formatos de exportación, la arquitectura puede considerarse particionada técnicamente.

Optar por el particionamiento de estructura técnico se recomienda cuando un proyecto exige una clara separación de responsabilidades, como la interfaz de usuario, la lógica de negocio y el acceso a datos, o si estás en un equipo pequeño que puede gestionar todos los componentes del sistema. Por ejemplo, una plataforma de gestión multi-cloud puede aprovechar el particionamiento técnico para soportar la integración con las APIs de múltiples proveedores de nube, permitiendo a los usuarios gestionar recursos a través de diferentes entornos de nube usando una única interfaz. De manera similar, un sistema de automatización de flujos de trabajo puede beneficiarse del particionamiento de estructura técnico soportando múltiples motores de flujo de trabajo y lenguajes de scripting, proporcionando flexibilidad para que los usuarios creen y ejecuten flujos de trabajo personalizados adaptados a sus necesidades específicas.

Particionamiento de estructura: Dominio

Este enfoque alinea la estructura de software con el modelo de dominio subyacente, haciéndolo más fácil de entender y razonar. El particionamiento de dominio promueve una relación estrecha entre la organización del sistema y el problema del mundo real que busca resolver. Ejemplos de estrategias de particionamiento de dominio incluyen:

  1. Microkernel: Como se explicó anteriormente, la arquitectura microkernel puede considerarse una combinación de particionamiento de estructura tanto técnico como de dominio debido a la forma en que organiza sus componentes. Desde la perspectiva del particionamiento de estructura de dominio, permite la organización del código por áreas de negocio. Cada plugin o módulo puede representar un dominio o funcionalidad de negocio específica, que puede desarrollarse, probarse y mantenerse de forma independiente de los demás. Por ejemplo, si los plugins representan diferentes módulos en un sistema de gestión del aprendizaje (LMS), como el seguimiento del progreso del estudiante, la evaluación en línea y la analítica del aprendizaje, la arquitectura puede considerarse una partición de dominio.
  2. Monolito modular (Modular Monolith): Organiza los componentes en módulos distintos y autocontenidos dentro de una única base de código, donde cada módulo representa un área funcional o dominio específico. Los componentes están diseñados para minimizar las dependencias y el acoplamiento entre módulos, lo que conduce a un sistema más mantenible, escalable y comprensible.
  3. Microservicios: Organiza los elementos en unidades compactas y autónomas centradas en un área de negocio o capacidad particular. Cada microservicio es responsable de sus propios datos, lógica y procesamiento, lo que permite el desarrollo, despliegue y escalado independientes.

El particionamiento de estructura de dominio es una opción adecuada cuando un proyecto se centra en modelar dominios de negocio complejos y quieres alinear la estructura de software con los conceptos y entidades del dominio subyacente, o cuando trabajas en un proyecto a gran escala con múltiples equipos, donde cada equipo es responsable de un área específica del dominio de negocio. Por ejemplo, un sistema de gestión de la salud puede beneficiarse del particionamiento de dominio dividiendo sus componentes en módulos distintos, como registros de pacientes, programación de citas y facturación, permitiendo a las personas desarrolladoras enfocarse en la funcionalidad específica de cada módulo sin afectar a los demás. Otro ejemplo podría ser un sistema de gestión de inventario que puede aprovechar el particionamiento de dominio separando los módulos responsables de la gestión del catálogo de productos y el procesamiento de pedidos, permitiendo que el sistema se extienda y adapte fácilmente para acomodar nuevos requisitos de negocio o cambios en los procesos existentes.

Casos de uso de estilo de arquitectura y particionamiento de estructura

Los estilos de arquitectura de software y las técnicas de particionamiento de estructura pueden aplicarse en diferentes casos de uso para abordar requisitos específicos del sistema. En esta sección vamos a compartir algunos casos de uso donde puede aplicarse. Como aclaración, las arquitecturas seleccionadas para los casos de uso representan una forma de implementar el sistema de software, hay múltiples maneras de hacerlo, por lo que recomendamos siempre tener en cuenta el alcance del proyecto (necesidades de negocio y requisitos del cliente) para tomar estas decisiones de arquitectura:

  1. Plataforma de e-commerce: Este tipo de plataforma normalmente ofrece servicios como listado de productos, gestión de inventario, gestión del carrito de compras, procesamiento de pedidos y gestión de cuentas de usuario. El estilo de arquitectura en capas podría ser adecuado para una plataforma de e-commerce, ya que promueve una limpia separación de responsabilidades organizando el código en capas distintas: presentación, lógica de negocio y acceso a datos. En este caso de uso, teniendo en cuenta el estilo de arquitectura en capas seleccionado, la arquitectura de monolito modular (particionamiento de estructura de dominio) podría usarse para dividir la aplicación en módulos bien definidos y desacoplados que se enfocan en capacidades de negocio específicas. Por ejemplo, la plataforma puede organizarse en módulos para catálogo de productos, inventario, carrito de compras, procesamiento de pedidos y gestión de usuarios. Cada módulo puede desarrollarse y mantenerse de forma independiente sin dejar de ser parte de un sistema único y cohesivo.
  2. Plataforma de reservas de viajes: Una plataforma de reservas de viajes normalmente requiere integración con múltiples sistemas externos, como sistemas de reservas de aerolíneas, sistemas de reservas de hoteles, servicios de alquiler de coches y pasarelas de pago. Un estilo de arquitectura orientada a servicios (SOA) es muy adecuado para este caso de uso, ya que organiza los componentes como servicios reutilizables e interoperables que se comunican a través de interfaces estándar. Una arquitectura de microservicios (particionamiento de estructura de dominio) puede usarse para gestionar las diversas funcionalidades descritas en la integración con sistemas externos. Este enfoque organiza los componentes en servicios pequeños y autónomos enfocados en capacidades de negocio específicas. Cada microservicio es responsable de sus propios datos, lógica y procesamiento, lo que permite el desarrollo, despliegue y escalado independientes.
  3. Plataforma de notificaciones: Una plataforma de notificaciones se caracteriza típicamente por la necesidad de procesar y reaccionar ante diferentes eventos asíncronos, como acciones del usuario, cambios de estado del sistema o disparadores externos. Una arquitectura dirigida por eventos es muy adecuada, ya que permite la comunicación asíncrona entre componentes, que puede manejar eficientemente el alto volumen de eventos y notificaciones. Una arquitectura de microservicios (particionamiento de estructura de dominio) podría usarse para gestionar las múltiples funcionalidades en un servicio de notificaciones, como el enrutamiento de mensajes, la gestión de usuarios, la entrega de mensajes y la analítica.
  4. Sistema de gestión de contenidos: Un sistema de gestión de contenidos (CMS) podría usar un estilo de arquitectura de microkernel o de plugins para permitir una extensión sencilla del sistema con nuevas funcionalidades como un tema personalizado, editor de contenido, búsqueda de contenido, gestión de usuarios, analítica, etc. Luego, en los módulos del kernel y los plugins, puede usarse el monolito modular (particionamiento de estructura de dominio) para organizar los componentes en módulos bien definidos.

Como puedes notar, hay múltiples combinaciones al seleccionar el estilo de arquitectura y el particionamiento de estructura. La elección no es estrictamente secuencial, ya que ambos aspectos son esenciales en el diseño de un sistema de software. Sin embargo, puede ser útil considerar primero el estilo arquitectónico, ya que proporciona una vista de alto nivel de la organización, comunicación y patrones de interacción del sistema. Luego, el particionamiento de estructura de arquitectura puede considerarse para refinar aún más la organización de los componentes o servicios dentro del sistema.

Casos de uso de estilo de arquitectura y particionamiento de estructura

Ejemplo de sistema TODO

En este ejemplo, diseñaremos un sistema TODO simple usando un estilo de arquitectura de software y una estrategia de particionamiento de estructura adecuados.

Para desarrollar el sistema TODO, estos son los requisitos que debemos cumplir:

  • Proporcionará una UI que se renderizará en el navegador con un diseño responsivo adecuado tanto para dispositivos de escritorio como móviles
  • Permitirá las siguientes operaciones: listar, crear, editar y eliminar.
  • El estado del TODO será: todo, in-progress, block y done.
  • La información se guardará en una base de datos relacional.
  • Los componentes principales contendrán pruebas automatizadas que se ejecutarán en un pipeline cada vez que se haga un cambio en el repositorio.

Selección del estilo de arquitectura de software

En esta sección vamos a analizar las opciones disponibles y cuál es la más adecuada para el sistema TODO teniendo en cuenta los requisitos del cliente.

Arquitectura orientada a servicios (SOA)

SOA típicamente implica la creación y gestión de múltiples servicios, cada uno exponiendo un conjunto específico de funcionalidades mediante interfaces bien definidas. En cambio, un sistema TODO es relativamente simple, consistente en operaciones CRUD básicas (Create, Read, Update, Delete) sobre los elementos TODO. Implementar SOA para tal sistema puede introducir complejidad y sobrecarga innecesarias. Además, implementar un sistema basado en SOA normalmente requiere más esfuerzo de desarrollo y experiencia, ya que se usa comúnmente para sistemas más grandes y distribuidos con múltiples servicios autónomos. La adopción de este estilo de arquitectura podría aumentar el tiempo y el costo de desarrollar y mantener el sistema TODO.

Arquitectura dirigida por eventos

La fortaleza principal de EDA radica en su comunicación asíncrona y no bloqueante. Sin embargo, un sistema TODO implica principalmente operaciones CRUD básicas, que pueden manejarse eficientemente usando patrones de comunicación síncronos de tipo solicitud-respuesta; además, el procesamiento de eventos puede introducir latencia debido al tiempo que toma que los eventos se publiquen, propaguen y consuman. La naturaleza asíncrona de EDA podría no proporcionar beneficios significativos para el sistema TODO.

Microkernel

Uno de los principales beneficios de la arquitectura microkernel es su extensibilidad, permitiendo la adición de nuevas funcionalidades a través de plugins sin modificar el sistema núcleo. Sin embargo, el alcance de un sistema TODO es generalmente limitado, y el requisito de una extensibilidad amplia podría necesitar ser más significativo para justificar la adopción de una arquitectura microkernel.

Microservicios

Los microservicios implican el despliegue y la gestión de múltiples servicios independientes, cada uno con sus propios requisitos de infraestructura. Si bien esto puede proporcionar beneficios como mayor escalabilidad y tolerancia a fallos, también aumenta el consumo de recursos de infraestructura para una aplicación relativamente simple como un sistema TODO. Además, en un sistema basado en microservicios, los servicios se comunican entre sí a través de una red, normalmente usando protocolos como REST o gRPC. Esta comunicación entre servicios puede introducir latencia, posibles cuellos de botella de red y complejidad adicional en términos de gestión de los patrones de comunicación y la consistencia de datos.

Monolítica modular

La arquitectura monolítica modular mantiene la simplicidad de una única aplicación, haciéndola más fácil de entender, desarrollar y mantener. Además, este estilo de arquitectura enfatiza la clara separación de responsabilidades y la modularización de los componentes dentro de la aplicación, permitiendo una mejor organización, mantenibilidad y extensibilidad. Este enfoque puede ayudar a gestionar la complejidad del sistema TODO sin dejar de mantenerlo fácil de entender y modificar.

En capas (Layered)

La arquitectura en capas promueve una clara separación de responsabilidades, haciéndola más fácil de entender, desarrollar y mantener el sistema. Cada capa se enfoca en un aspecto específico, como la presentación, la lógica de negocio o el acceso a datos, permitiendo una mejor organización y modularización. Con una clara separación entre capas, resulta más fácil modificar o actualizar componentes individuales sin afectar todo el sistema. Esto mejora la mantenibilidad y permite adaptaciones o mejoras más directas en el futuro.

Resultado del trade-off

Al comparar los estilos de arquitectura como En capas, SOA, EDA, Microkernel y Microservicios, la arquitectura en capas proporciona un enfoque más simple y directo que se alinea bien con los requisitos de un sistema TODO básico (UI, backend y base de datos). Mientras que SOA, EDA y Microservicios son más adecuados para sistemas complejos, distribuidos y altamente escalables, introducen complejidad y sobrecarga adicionales que podrían no ser necesarias para un sistema TODO simple. La arquitectura microkernel, por otro lado, es más relevante para sistemas con funcionalidad núcleo que puede extenderse a través de plugins, lo cual no es un requisito principal para el sistema TODO. Comparando la arquitectura en capas con la arquitectura monolítica modular, ambos enfoques ofrecen modularidad, mantenibilidad y una organización clara. Sin embargo, la arquitectura en capas proporciona una separación de responsabilidades más explícita, dividiendo el sistema en capas distintas responsables de la UI, el servicio backend y las interacciones con la base de datos. Esta separación simplifica el desarrollo, las pruebas y el mantenimiento, facilitando la evolución del sistema con el tiempo. Como resultado, la arquitectura en capas es una opción más adecuada para un sistema TODO, ya que logra el equilibrio adecuado entre simplicidad, mantenibilidad y facilidad de desarrollo, sin dejar de ofrecer la funcionalidad requerida.

Selección del particionamiento de estructura de arquitectura de software

Al considerar un sistema TODO que requiere una UI que se comunica con un servicio backend que interactúa con una base de datos, y habiendo seleccionado el estilo de arquitectura en capas, es esencial evaluar las opciones de particionamiento de estructura de arquitectura disponibles. Como explicamos en los apartados anteriores, hay dos tipos principales de particionamiento de estructura: particionamiento técnico y de dominio. El particionamiento técnico se enfoca en separar el código según la funcionalidad, mientras que el particionamiento de dominio enfatiza dividir el código por áreas de negocio o funcionalidades.

Comparando el particionamiento técnico con el particionamiento de dominio en el contexto del estilo de arquitectura en capas, el particionamiento técnico se alinea bien con la separación de responsabilidades inherente que proporciona el enfoque en capas. En el caso del sistema TODO, el particionamiento técnico en capas podría asegurar una clara separación de las capas de UI, servicio backend e interacción con la base de datos, lo que simplifica el desarrollo, las pruebas y el mantenimiento. Las opciones de arquitecturas de particionamiento de dominio, aunque útiles para sistemas más grandes con múltiples áreas de negocio o funcionalidades, podrían no ser prácticas para un sistema TODO simple. Con base en el análisis anterior, usaremos el particionamiento de estructura en capas para la implementación del sistema TODO.

Con el estilo y el particionamiento de estructura seleccionados, el sistema estará compuesto por las siguientes capas:

  1. Capa de presentación:
    • Esta capa será responsable de renderizar la interfaz de usuario (UI) en el navegador, manejar las interacciones del usuario y presentar los datos al usuario.
    • Tecnologías: HTML, CSS, JavaScript y un framework de frontend como Next, React, Angular o Vue.js pueden usarse para construir la UI.
  2. Capa de negocio:
    • Esta capa contendrá la lógica de negocio para el sistema TODO, manejando las solicitudes del usuario y coordinando las interacciones entre las capas de presentación y persistencia.
    • Tecnologías: Un framework de backend como Express.js (Node.js), Flask (Python) o Spring Boot (Java) puede usarse para implementar la capa de negocio.
  3. Capa de persistencia:
    • Esta capa será responsable de comunicarse con la base de datos relacional PostgreSQL para almacenar, recuperar y gestionar los datos del TODO.
    • Tecnologías: Una librería de mapeo objeto-relacional (ORM) como Sequelize (Node.js), SQLAlchemy (Python) o Hibernate (Java) puede usarse para interactuar con la base de datos PostgreSQL.

Las funcionalidades del sistema TODO se implementarán de la siguiente manera:

  • Listar: La capa de negocio recupera la lista de TODOs de la capa de persistencia y envía los datos a la capa de presentación para su visualización.
  • Crear: La capa de presentación captura la entrada del usuario y envía una solicitud a la capa de negocio, que valida la entrada y llama a la capa de persistencia para almacenar el nuevo TODO en la base de datos PostgreSQL.
  • Editar: La capa de presentación envía una solicitud de actualización a la capa de negocio con los datos del TODO modificados, que luego actualiza el registro correspondiente en la base de datos usando la capa de persistencia.
  • Eliminar: La capa de presentación envía una solicitud de eliminación a la capa de negocio, que luego elimina el registro correspondiente de la base de datos usando la capa de persistencia.

Al seleccionar el estilo de arquitectura en capas y el particionamiento de estructura técnico para el sistema TODO, hemos creado un diseño modular, mantenible y bien organizado que puede extenderse o modificarse fácilmente para acomodar nuevas funcionalidades o requisitos.

Arquitectura en capas del sistema TODO

Puedes encontrar el ejemplo de implementación del código en los siguientes enlaces:

Diagramas de arquitectura de software

Cuando llega el momento de diagramar en detalle los componentes de la arquitectura de software, hay múltiples enfoques disponibles para crear diagramas detallados, que ofrecen sus propios beneficios y ventajas. Estos métodos buscan capturar diferentes aspectos del sistema, como componentes, relaciones e interacciones, para facilitar la comprensión y la comunicación entre los stakeholders. Algunas técnicas de diagramación populares incluyen el Lenguaje Unificado de Modelado (UML), el modelo C4, los diagramas de flujo y los diagramas de flujo de datos.

En mi experiencia, el modelo C4 es popular y muy usado por los arquitectos de software para diagramar en detalle las arquitecturas de software, ya que incorpora múltiples niveles de abstracción, incluyendo nivel 1 - contexto del sistema, nivel 2 - contenedor, nivel 3 - componente y nivel 4 - código. La mayoría de los clientes normalmente hacen los diagramas hasta el nivel 3, que es el diagrama de componentes, ya que es bueno para documentación de larga vida y la audiencia son arquitectos y desarrolladores. El nivel 4 se considera opcional, ya que proporciona documentación de corta vida que puede autogenerarse mediante los entornos de desarrollo integrados (IDEs).

Para nuestro sistema TODO, vamos a crear diagramas C4 pero solo hasta el nivel 3, ya que el código no está listo para producción y debe adaptarse o usarse solo como un ejemplo de referencia, lo que significa que el nivel 4 es altamente susceptible a cambios.

Nivel 1: Diagrama de contexto del sistema

El diagrama de contexto del sistema de nivel 1 en el modelo C4 proporciona una vista de alto nivel de todo el sistema, ilustrando sus interacciones con los clientes y los sistemas externos. En nuestro sistema TODO, no estamos integrando sistemas externos como métodos de autenticación o notificación, así que nuestro contexto principal en este nivel será el sistema y el cliente.

  • Sistema: Representa todo el sistema TODO como una sola entidad, encapsulando todos sus componentes y funcionalidades.
  • Cliente: Representa al usuario que interactúa con el sistema TODO.
Nivel 1: Diagrama de contexto del sistema - Modelo C4

Nivel 2: Diagrama de contenedores

Este nivel se enfoca en los diferentes contenedores dentro del sistema, mostrando sus responsabilidades y cómo interactúan entre sí. Nuestro sistema TODO en este nivel estará compuesto por los siguientes contenedores:

  • App web (interfaz de usuario): Incluye la aplicación web que sirve como la interfaz del cliente para interactuar con la app API. Este contenedor gestiona la entrada del cliente, muestra las tareas y se comunica con los servicios backend.
  • App API: Este contenedor es responsable de procesar las solicitudes del cliente, gestionar las tareas e interactuar con la capa de almacenamiento de datos.
  • Base de datos: El sistema de almacenamiento de datos se usa para almacenar los datos del TODO
Nivel 2: Diagrama de contenedores - Modelo C4

Nivel 3: Diagrama de componentes

En el sistema TODO, el diagrama de componentes de nivel 3 se enfoca en la estructura interna y las interacciones de los componentes principales dentro del sistema, así que está compuesto por las siguientes partes.

App web (UI):

Este contenedor gestiona la capa de presentación de la aplicación, manejando la entrada del cliente y mostrando los datos de la app, como tareas, fechas de vencimiento y estado de completitud; tiene los siguientes componentes.

  • Componente de página (Page): Responsable de componer el diseño y la estructura de una página específica, ensamblando los componentes necesarios y manejando cualquier lógica específica de la página.
  • Componentes: Implementan elementos de UI reutilizables y manejan la lógica asociada, como la entrada del usuario, la visualización de datos y las interacciones. Ejemplos: ConfirmationModal.tsx, ErrorBoundary.tsx, TodoForm.tsx, TodoItem.tsx, TodoList.tsx.
  • Componente de contextos (Contexts): Supervisan la gestión del estado de la aplicación, manejan la lógica de negocio y proporcionan una solución de gestión de estado global, permitiendo un flujo de datos y un compartimiento de estado eficientes a través de la aplicación.
  • Componente de servicios (Services): Facilitan la comunicación con fuentes de datos externas, como APIs o bases de datos, y ejecutan operaciones CRUD. Estos componentes encapsulan la lógica de recuperación, creación, actualización y eliminación de datos, aislándola del resto de la aplicación.

App API

En este contenedor, los endpoints de la API manejan las solicitudes HTTP entrantes y proporcionan servicios RESTful para gestionar los elementos todo; está compuesto por las siguientes partes:

  • Componente de rutas (Routes): Este componente gestiona las rutas de la API de la app API, proporcionando una interfaz para que las aplicaciones cliente interactúen con el sistema. Define los métodos HTTP (p. ej., GET, POST, PUT, DELETE) y las URLs correspondientes para diferentes operaciones, como crear, actualizar, eliminar o consultar TODOs. El componente de rutas maneja las solicitudes entrantes, dirigiéndolas a los componentes de servicio dentro de la aplicación para su posterior procesamiento. También asegura que se envíen las respuestas adecuadas de vuelta al cliente, conteniendo los datos o códigos de estado necesarios.
  • Componente de servicio (Service): Responsable de la funcionalidad principal de la app API, este componente gestiona la creación, modificación, eliminación y completitud de TODOs. También maneja el procesamiento de cualquier lógica de negocio o reglas asociadas con las tareas.
  • Componente de migraciones (Migrations): Este componente se encarga de manejar los cambios y actualizaciones del esquema de la base de datos para la app API. Mantiene un historial de versiones del esquema de la base de datos, permitiendo transiciones fluidas entre diferentes versiones a medida que la aplicación evoluciona. Al usar el componente de migraciones, las personas desarrolladoras pueden automatizar el proceso de aplicar actualizaciones del esquema, asegurando que la base de datos se mantenga sincronizada con los requisitos de la aplicación.
  • Componente de base de datos (Database): Responsable de persistir los TODOs y sus datos asociados, asegurando que la información se almacene y recupere de una fuente de datos (p. ej., una base de datos o almacenamiento en memoria como sqlite) según sea necesario.
Nivel 3: Diagrama de componentes - Modelo C4

Bonus: Arquitectura hexagonal

La arquitectura hexagonal también es conocida como puertos y adaptadores (ports and adapters); en el apartado anterior no la presenté como un estilo de arquitectura, ya que la considero un patrón de arquitectura con un conjunto de principios que se enfoca en un aspecto específico del diseño de software: la separación de responsabilidades y el desacoplamiento de componentes. Así que, como patrón, puede aplicarse a diferentes estilos de arquitectura. Permíteme presentarte algunos ejemplos de combinación de la arquitectura hexagonal con diferentes estilos de arquitectura:

Arquitectura en capas

La arquitectura en capas organiza los componentes en capas distintas, como presentación, negocio y acceso a datos. Aplicando los principios de la arquitectura hexagonal, puedes mejorar la separación de responsabilidades introduciendo puertos y adaptadores para gestionar las dependencias entre capas. La lógica de negocio principal permanece en la capa de dominio, mientras que la capa de aplicación define los puertos necesarios para diversas interacciones. Las capas de presentación y acceso a datos pueden considerarse adaptadores que implementan los puertos definidos.

Microservicios

En una arquitectura de microservicios, los componentes se organizan en servicios pequeños y autónomos enfocados en dominios de negocio específicos. La arquitectura hexagonal puede aplicarse a cada microservicio individualmente, manteniendo la lógica de dominio principal separada de las dependencias externas a través de puertos y adaptadores. Esto puede mejorar la mantenibilidad y adaptabilidad de los microservicios individuales, facilitando el cambio o reemplazo de partes específicas del sistema. Por ejemplo, al crear un microservicio para procesar pagos, la lógica de negocio principal maneja las reglas de procesamiento de pagos, mientras que los adaptadores manejan las interacciones con sistemas externos como pasarelas de pago y notificaciones de usuario.

Arquitectura dirigida por eventos

EDA se enfoca en la comunicación asíncrona de eventos entre componentes. La arquitectura hexagonal puede aplicarse para separar la lógica de negocio principal de los componentes de procesamiento de eventos y los sistemas externos. Los publicadores, suscriptores y manejadores de eventos pueden implementarse como adaptadores que interactúan con el núcleo a través de los puertos definidos. Esto permite un mejor desacoplamiento de los componentes y más flexibilidad en el manejo de eventos.

Monolito modular

Un monolito modular es un sistema monolítico organizado en módulos con límites claros y separación de responsabilidades. Aplicar la arquitectura hexagonal puede mejorar aún más la modularidad aislando la lógica de dominio principal de cada módulo y usando puertos y adaptadores para gestionar las dependencias entre módulos y sistemas externos. Por ejemplo, en una aplicación de e-commerce, módulos como la gestión de productos, la gestión de clientes y el procesamiento de pedidos pueden diseñarse usando la arquitectura hexagonal, con adaptadores para la comunicación entre módulos y dependencias externas como bases de datos o APIs de terceros.

Microkernel

La arquitectura microkernel organiza los componentes en un sistema núcleo (microkernel) y un conjunto de plugins o extensiones. Combinar la arquitectura microkernel con la arquitectura hexagonal permite una clara separación de la funcionalidad principal y las funcionalidades específicas del dominio. En este enfoque, el microkernel gestiona la funcionalidad central, y los plugins o extensiones implementan funcionalidades adicionales usando la arquitectura hexagonal. Por ejemplo, en el CMS, el microkernel podría manejar el almacenamiento y la recuperación básicos de contenido, mientras que los plugins para diferentes formatos de contenido como imágenes, videos y documentos se desarrollan usando la arquitectura hexagonal. Cada plugin tiene puertos de entrada y salida para la comunicación con el microkernel y los sistemas externos, y los adaptadores traducen los datos o las solicitudes entre el microkernel, los sistemas externos y los plugins.

Conclusión

Esta publicación ofrece una visión general de alto nivel para determinar —según el contexto— el estilo arquitectónico y el particionamiento de estructura adecuados. Presentamos diferentes ejemplos de casos de uso con contexto limitado para ilustrar la idoneidad de estilos de arquitectura y particionamientos de estructura específicos. Además, se mostró un ejemplo de código de un sistema TODO con el fin de demostrar el diseño, la implementación y los diagramas usando el modelo C4. Espero que esta publicación brinde algunas ideas al enfrentar el reto de seleccionar una arquitectura de software para el sistema que estás desarrollando.