Resumen Completo de Arquitectura de Software

258 Visitas 60 minutos Enlace Permanente

"Crear software es muy fácil, crear software de calidad es muy difícil." - Oscar Blancarte. "El programador sabe qué se puede hacer, el arquitecto sabe que se debería hacer" - Memi Lavi

"Crear software es muy fácil, crear software de calidad es muy difícil." - Oscar Blancarte. "El programador sabe qué se puede hacer, el arquitecto sabe que se debería hacer" - Memi Lavi

Definiciones

  • Requerimientos:
  • Funcionales: lo que pide el cliente. [Explícitos]
  • No Funcionales: lo que presupone el cliente (rápido, seguro, escalable, etc.). [Implícitos]
  • Restricciones: impuestas por terceros o por el entorno (ej. legales)
  • Atributos de Calidad: requisitos no funcionales.
  • Arquitectura de Software: define la estructura de un sistema, los componentes que lo conforman y la relación que existe entre ellos (mediante patrones y abstracciones) para permitir su implementación, su mantenimiento y asegurar la calidad del mismo.
  • Patrones:
  • De Diseño: solución efectiva y reusable de un problema de diseño de clases dentro de un componente.
  • De Arquitectura: solución efectiva y reusable de un problema de diseño de componentes dentro de la arquitectura de software de un sistema.
  • Estilos Arquitectónicos: marcos de referencia que determinan ciertas características que deben poseer los componentes de un patrón de arquitectura.
  • Otros Tipos de Arquitectos:
  • Arquitecto de Empresa: CTO en contacto directo con CEO, no desarrolla (solo gestiona).
  • Arquitecto de Infraestructura: servidores, redes, almacenamiento, etc. (devops).
  • Organigramas Típicos:
  • Arquitecto por Proyecto: arquitectos junior

CTO → Servicios IT, [Jefe de Proyecto → [Arquitecto, [Jefe de Programación → Programadores]]]

  • Arquitecto Único (varios proyectos): arquitectos senior

CTO → Servicios IT, Arquitecto, [Jefe de Proyecto → Jefe de Programación → Programadores]

  • Combinación de Ambos: un arquitecto por proyecto y uno único por encima.
  • A definir en una Arquitectura del Sistema:
  • Acoplamiento (bajo).
  • Estados (mínimo o ninguno).
  • Caché.
  • Mensajería.
  • Logs y monitorización.
  • Tipos de Aplicaciones:
  • Gráficas (Frontend): escritorio, web, móvil.
  • Procesos (Backend): API, consola, servicio.
  • Secciones Básicas del Documento de Arquitectura:
  • Trasfondo y motivaciones (1 pág.)
  • Requerimientos (Funcionales y No Funcionales: 1 pág., 2-3 líneas por cada uno).
  • Resumen ejecutivo (para directivos, con gráficos y diagramas, 3 págs.)
  • Resumen de la Arquitectura (10 págs.)
  • Componentes Detallados (100 págs.)

Conceptos Básicos

  • Encapsulación:
  • Los componentes proporcionarán métodos, servicios o APIs que permitan consultar o manipular datos de forma segura, evitando que desde fuera se pueda acceder a ellos directamente.
  • También permite definir abstracciones, ocultando detalles irrelevantes.
  • Acoplamiento:
  • Es el nivel de dependencia entre 2 componentes.
  • Hay que evitar el alto acoplamiento. Cuanto más desacoplados, mejor.
  • Bajo Acoplamiento: cuando un componente no conoce o conoce muy poco del funcionamiento interno de otros componentes.
  • Cohesión:
  • Media de relación funcional entre las partes de un componente.
  • Cada componente debe realizar una única tarea.
  • Los componentes deben ser altamente cohesivos.
  • DRY (Don't Repeat Yourself): se debe evitar duplicar código o funcionalidad.
  • YAGNI (You Aren't Gonna Need It): solo programar lo necesario, sin agregar nada extra.
  • SoC (Separation of Concerns):
  • Los programas se deben separar en secciones distintas (ej. capas).
  • Cada sección será un conjunto de componentes relacionados.
  • Cada componente puede dividirse, a su vez, en conjuntos de clases relacionadas.
  • Ley de Demeter:
  • Se debe conocer lo mínimo posible de otra clase o un componente.
  • No hables con extraños: sólo la clase y las clases de sus propiedades y métodos, no más allá.
  • KISS (Keep It Simple, Stupid!): la mejor solución siempre es la más simple.
  • IoC (Inversion of Control):
  • Control Normal:
  • Un framework no conoce nada del programa que lo usa.
  • El framework sólo expone métodos que pueden ser ejecutados.
  • El programa conoce todos los detalles del framework y las operaciones a realizar con él.
  • Inversión del Control:
  • Es el framework el que ejecuta operaciones sobre el programa.
  • Se suele implementar con inyección de dependencias e interfaces.
  • También se puede utilizar la reflexión/introspección (evitarlo si se puede).
  • Principios SOLID:
  • Single Responsibility: cada elemento debe hacer 1 sola cosa, tener 1 única responsabilidad.
  • Open / Close: un elemento debe estar abierto a ampliarse pero cerrado a modificarse.
  • Liskov Substitution: cada elemento de un tipo debe poderse sustituir por uno de sus subtipos (ej. polimorfismo de clases).
  • Interface Segregation: cada elemento debe conocer de otro sólo las partes que va a usar, nada más. Se implementan muchas interfaces con muy pocos métodos, pero muy cohesivos (interfaces de rol).
  • Dependency Inversion:
  • Los elementos de alto nivel no pueden depender de los de bajo nivel, sino de abstracciones.
  • Las abstracciones (ej. interfaces) no deben depender de los detalles, sino al revés.
  • La alternativa es el uso de Factories.

Requerimientos

“Los requerimientos indican lo que el sistema debería hacer. Los objetivos indican cómo ayudará el sistema a la organización”.

  • Pasos:
  1. Establecer los objetivos (junto a los directivos).
  2. Entender los requerimientos del sistema (junto al analista del sistema y jefe del proyecto).
  3. Entender los requerimientos no funcionales (atributos de calidad, guiar/preguntar al cliente).
  4. Crear mapa de componentes / servicios (demostrar al cliente que lo entiendes todo bien).
  5. Seleccionar la tecnología (para cada componente).
  6. Diseñar la arquitectura (todos los detalles, junto al equipo de desarrollo):
  • Infraestructura.
  • Plataforma.
  • Módulos / Clases.
  1. Escribir la documentación.
  2. Dar soporte al equipo de desarrollo.
  • Funcionales: hay que analizar los siguientes aspectos:
  1. Flujos del negocio.
  2. Servicios del negocio.
  3. Interfaces de usuario.
  • No Funcionales: atributos de calidad, los más importantes tienen que ver con estos aspectos:
  1. Rendimiento: nº tareas por unidad de tiempo, tiempos normal y máximo de respuesta/ejecución.
  2. Carga: nº de tareas simultáneas sin colapsar el sistema.
  3. Volumen: de datos (bytes): tipo de almacén, tipo de queries, cantidad a almacenar (inicial y crecimiento).
  4. Concurrencia de usuarios (suele ser 10 veces la carga).
  5. SLA: porcentaje de tiempo funcionando (ej. 99,9%). [100% es imposible, hay que ser realistas]
  • Puntos Clave:
  1. Rendimiento: latencia (CPU, RAM, disco, red, procesado), asincronía, concurrencia y caché. [carga fija]
  2. Escalado: [carga variable]
  • Vertical: CPU, RAM, disco, red (limitado). Es mejor escalar horizontalmente.
  • Replicación: servicios (stateless) o datos (stateful): CQRS, backup, P2P, particionado de BD.
  • Caché, procesado asíncrono (colas de mensajes), comandos/eventos y microservicios.
  • Balanceo de Carga: balanceador (proxy inverso), registro de servicios, DNS.
  1. Confianza: SLA, redundancia, monitoreo, recuperación (hot, warm, backup), tolerancia a fallos parciales.
  2. Seguridad: cifrado, firma, firewall, autenticación, autorización, tokens (SSO, JWT), roles, ataques.
  3. Despliegue: VM, docker, kubernetes, rolling, canary, blue-green, recreate, A/B testing, staging, devops.

Atributos de Calidad

Resumen: un sistema debe ser rápido, seguro, confiable y fácil de mantener.

Observables

Todos los que pueden ser observados y validados por el usuario final con tan solo usar el sistema.

  • Rendimiento: tiempos de respuesta: nº de transacciones por unidad de tiempo. Depende de:
  • Nº de usuarios/procesos simultáneos.
  • Cantidad de datos a transmitir.
  • Latencias (red, lectura/escritura, servicios externos, etc.)
  • Anchos de banda.
  • Procesadores y memorias.
  • Picos puntuales.
  • Seguridad: evitar el uso no autorizado (OWASP). Un sistema seguro debe tener:
  • No repudio (logs, certificado, cifrado).
  • Autenticación (certificado).
  • Confidencialidad (cifrado).
  • Integridad (firma digital).
  • Auditoría (logs).
  • Disponible (SLA): porcentaje de tiempo en funcionamiento (tiempo funcionando / tiempo total).
  • Alta disponibilidad: igual o mayor que 99,999%.
  • Cómo llevarlo a cabo: balanceo de carga, redundancia, alertas, planes de contingencia, etc.
  • Funcional: realizar el trabajo para el cual el sistema fue diseñado. Para asegurarlo se usan:
  • Pruebas unitarias.
  • Pruebas de integración.
  • Pruebas de usuario (end-to-end).
  • Usable: satisfacción del usuario con el sistema:
  • Tiempo de aprendizaje (curva).
  • Facilidad de lectura, visualización y contraste de los elementos.
  • Número de clicks para hacer una tarea.
  • Internacionalización, localización y accesibilidad.
  • Confiable: seguridad del usuario de que no le fallará cuando lo necesite.
  • Número de fallos o cuelgues por unidad de tiempo.
  • Facilidad de instalación, actualización y desinstalación.
  • Interoperable: intercambio de datos con otros sistemas.
  • Tipos de archivos que es capaz de leer, importar o exportar.
  • API que expone al exterior.
  • Plugins o extensiones.
  • Desplegable: facilidad para nuevas instalaciones o realizar actualizaciones.
  • Número de pasos para instalarlo y complejidad de los mismos.
  • Necesidades de conocimientos técnicos para llevarlo a cabo (dependencias con terceros).
  • Tiempos de parada para actualizarlo y número de veces que hay que hacerlo por unidad de tiempo.

No Observables

  • Modificable: costo y riesgo de realizar un cambio.
  • ¿Qué puede cambiar?
  • ¿Cuál es la probabilidad de dicho cambio?
  • ¿Quién debe hacer el cambio?
  • ¿Cuánto cuesta el cambio, en tiempo y dinero?
  • Mantenible: costo y tiempos de mantenimiento (sin hacer cambios en el código).
  • ¿Qué tareas deben realizar los usuarios asiduamente?
  • ¿Opera de forma automática o necesita de intervención humana?
  • ¿Requiere de cambios en la configuración a menudo?
  • Portable: adaptarse a otra plataforma.
  • Lenguajes: Java, Python, etc.
  • Máquinas virtuales y contenedores.
  • Recompilados para diversos sistema.
  • Uso de APIs.
  • Uso de estándares.
  • Reusable: poderse usar en más de un programa.
  • Librerías y paquetes
  • Documentación
  • Principios SOLID, GRASP, etc.
  • REST, JSON, SOA, SOAP, XML, microservicios, etc.
  • Testable: cómo de fácil es hacer pruebas para que los errores salgan a la luz lo antes posible.
  • Pruebas unitarias.
  • Pruebas de integración.
  • Pruebas de usuario (extremo a extremo).
  • Escalable: adaptación del sistema al aumento de carga.
  • Vertical: aumento del hardware del servidor.
  • Horizontal: aumento del nº de servidores (clusters, contenedores).
  • Uso de Estándares: número de estándares con los que se cumple.
  • Desarrollo Ágil: cómo se adapta a las metodologías ágiles tipo Kanban o Scrum.
  • Desarrollo Fácil: tiempos de aprendizaje y de desarrollos, si se puede distribuir el desarrollo, etc.

Estilos Arquitectónicos

Monolítico

  • Características:
  • Independiente: cuando se compila, se empaqueta todo como una única pieza.
  • Autosuficiente: contiene toda la funcionalidad sin dependencias externas.
  • Datos Privados: cada instalación tiene su propia BD.
  • Una Única Plataforma: sus componentes comparten los mismos recursos y memoria.
  • Ventajas:
  • Fácil de desarrollar, poner en producción y escalar.
  • Pocos puntos de fallo y fácil de probar, al tener muy pocas dependencias.
  • Buen rendimiento, al no tener procesos distribuidos.
  • Inconvenientes:
  • Muy dependiente de sus tecnologías de desarrollo.
  • El escalado es total, no sólo de las partes que son más críticas.
  • Altas dependencias entre desarrolladores, lo que modifica uno puede afectar a otros muchos.
  • Actualizaciones muy grandes, cualquier pequeño cambio requiere sacar una nueva versión completa.
  • Es más fácil caer en malas prácticas de programación.
  • Puede ser abrumador para nuevas incorporaciones al equipo.
  • Cuándo Usarlo:
  • Comenzar siempre como monolito y luego llevarlo a otra arquitectura cuando sea necesario.
  • Para productos mínimos viables y prototipos, permite sacar algo rápidamente al mercado.

Cliente-Servidor

  • Características:
  • 2 componentes bien diferenciados: proveedor y consumidor.
  • El proveedor aporta los servicios y recursos que usan uno o varios consumidores.
  • La comunicación entre ellos se realiza a través de una API sobre TCP/IP.
  • Los servidores y los clientes se construyen de forma independiente, como monolitos.
  • Pueden usar librerías comunes (DTOs, interfaces, clases base, utilidades, etc.)
  • Ventajas:
  • Centralización: toda la funcionalidad y datos en un único sitio.
  • Seguridad: no sólo por software, sino también por hardware (firewalls, subredes, etc.)
  • Cliente fácil de instalar.
  • Separar Responsabilidades: lógica de negocio en servidor, presentación en cliente.
  • Portable: el servidor y el cliente pueden correr en diferentes plataformas.
  • Inconvenientes:
  • Actualizar Clientes: hay que actualizar muchos y quizá a la misma vez.
  • Concurrencia: peticiones simultáneas hacia el servidor.
  • Dependencia: si el servidor o la red se caen, el cliente no puede operar.
  • Depuración: al estar en diferentes procesos, es más difícil analizar los errores.
  • Cuándo Usarlo:
  • Se usa muy poco, porque ha quedado desfasada en la mayoría de los casos.
  • Aplicaciones de tiempo real, que deben mostrar los datos al instante.
  • Aplicaciones centralizadas de alto rendimiento.

Distribuido (P2P: Peer-To-Peer)

  • Características:
  • Red donde cada nodo actúa como cliente y servidor a la vez.
  • Hay diferentes grados de descentralización:
  • Puro No Estructurado: red totalmente descentralizada (mucho tráfico de red, ej. FreeNet).
  • Puro Estructurado: se comparten índices distribuidos de recursos (ej. Kademlia).
  • Híbrido: los índices están en varios servidores distribuidos (ej. eMule, BitTorrent).
  • Cuanto más nodos, más capacidad de procesamiento.
  • Ventajas:
  • Muy Escalable: es tan fácil de escalar como incluir nuevos nodos.
  • Alta Tolerancia a Fallos: la red sigue funcionando aunque algunos nodos se caigan.
  • Descentralización: no necesitan que nadie organice la red.
  • Privacidad: muy difícil de saber qué nodo realizó la petición original.
  • Carga Equilibrada: entre todos los nodos de la red por igual.
  • Inconvenientes:
  • Alta Complejidad: búsquedas eficientes de recursos, funcionando como cliente y servidor a la vez.
  • Descontrol: no se puede controlar los contenidos que circulan por la red.
  • Baja Seguridad: lo que permite que se cuele código malicioso.
  • Alto Tráfico: se puede saturar la red por el alto número de comunicaciones entre los nodos.
  • Cuándo Usarlo:
  • Compartir archivos, realizar streaming o llamadas VoIP.
  • Procesamiento distribuido y blockchain.

Arquitectura en Capas (N-Capas)

  • Características:
  • Componentes agrupados en varias capas horizontales.
  • Cada capa sólo se comunica con su capa inmediatamente superior o inferior.
  • Cada capa sólo depende de la capa inmediatamente inferior.
  • Cada capa debe ser independiente y se debe poder desplegar por separado.
  • Ventajas:
  • Separar Responsabilidades: cada capa tiene una única responsabilidad.
  • Desarrollo Fácil: lo usan la gran mayoría de las aplicaciones.
  • Fácil de Probar: se puede testear cada capa de forma individual.
  • Fácil de Mantener: se sabe de qué capa viene el bug y donde hay que aplicar el cambio.
  • Seguridad: se podría separar cada capa en subredes diferentes.
  • Inconvenientes:
  • Menor Rendimiento: hay que realizar más pasos que si se hace directamente.
  • Escala Peor: sólo admite el escalado monolítico.
  • Despliegue Complejo: un pequeño cambio puede requerir un despliegue completo.
  • Muy dependiente de sus tecnologías de desarrollo.
  • Tolerancia a Fallos: si una capa falla, las superiores fallan todas en cascada.
  • Cuándo Usarlo:
  • El monolítico por capas se suele considerar la arquitectura por defecto para cualquier proyecto.
  • Si no se sabe bien qué arquitectura usar, lo mejor es elegir esta.

Microkernel

  • Características:
  • Aplicaciones mínimas (core) al que se le agregan pequeños plugins (extensiones y personalizaciones).
  • El sistema core proporciona una API e instrucciones muy claras para crear y montar plugins.
  • La API suele definir interfaces que deben implementar las clases de los plugins.
  • Instalación por Descriptores: archivo XML, JSON, YAML, etc. con metadatos del plugin.
  • Instalación por Metadatos: usa inversión del control, introspección y metadatos en las propias clases.
  • Los plugins se suelen almacenar y centralizar en repositorios.
  • Ventajas:
  • Tests: se puede probar el core y cada plugin de forma independiente.
  • Rendimiento: el core suele ser monolítico.
  • Despliegue: no se requiere de reinicios para ampliar el software.
  • Dinamismo: los plugins se pueden activar o desactivar, y se puede personalizar para cada cliente.
  • Modular: varios equipos pueden trabajar en paralelo creando plugins.
  • Reutilización: los plugins se pueden instalar en cualquier instancia del sistema core.
  • Inconvenientes:
  • Escalabilidad: los microkernels suelen trabajar en modo standalone y no están pensados para escalar.
  • Complejidad: es difícil de desarrollar ya que requieren de un gran análisis y tener mucho cuidado.
  • Cuándo Usarlo:
  • Gran Escala: como los IDEs, las apps ofimáticas o los navegadores.

SOA (Service-Oriented Architecture)

  • Características:
  • Se originaron con RPC y CORBA, para intercambio de datos entre aplicaciones independientes.
  • SOA usa estándares abiertos y APIs SOAP para comunicar aplicaciones vía HTTP.
  • Servicios Web:
  • Protocolos y estándares abiertos para intercambio de información vía web.
  • Cada servicio web es independiente y puede usar un lenguaje de programación distinto.
  • Expone un WSDL de sus operaciones e incluye XSD con sus tipos de datos.
  • Reutilización:
  • Cada servicio hace 1 única tarea.
  • Cada servicio se puede apoyar en otros servicios creando servicios de alto nivel.
  • Encapsulamiento:
  • Oculta la complejidad mediante servicios de alto nivel.
  • Evita que se acceda directamente a las tablas y datos internos del sistema.
  • Enterprise Service Bus (ESB):
  • Componente que permite crear abstracciones de los servicios expuestos.
  • Las apps se comunican con el ESB (intermediario) en vez de los servicios directamente.
  • Permite bajar el acoplamiento entre servicios, centralizar la seguridad, etc.
  • Ventajas:
  • Bajo Acoplamiento: por usar contratos (interfaces) y ESB.
  • Tests: se puede probar cada servicio de forma independiente.
  • Reutilización: realizan 1 única tarea.
  • Agilidad: se pueden crear nuevos servicios rápidamente.
  • Escalabilidad: agregando nuevas instancias del servicio y balanceando carga en ESB.
  • Interoperable: comunicación entre aplicaciones heterogéneas.
  • Inconvenientes:
  • Rendimiento: la comunicación en vía web y el uso de otros servicios agrega latencia.
  • Volumetría: no es adecuado para manejar grandes volúmenes de información.
  • Gobierno: la empresa debe tener bien organizada la creación y uso de servicios.
  • Más Puntos de Falla: la caída de un servicio puede provocar una falla en cascada.
  • Cuándo Usarlo:
  • Si se necesita integrar la aplicación con otras aplicaciones.
  • No se debería usar si:
  • Se van a procesar o enviar grandes cantidades de datos.
  • Si se requiere un alto rendimiento.
  • Si no existe una buena organización en cuanto a creación y uso de servicios.

Microservicios

  • Características:
  • Pequeño componente que sólo hace una única tarea de forma autosuficiente.
  • Suelen tener su propia base de datos, independiente del resto de microservicios.
  • Cada microservicio se puede implementar con una tecnología diferente.
  • Los microservicios se comunican entre sí para realizar tareas más complejas.
  • Se despliegan y actualizan de forma independiente y no requieren de mucho hardware.
  • No tienen que exponer servicios siempre, pueden realizar una tarea útil dentro del sistema.
  • API Gateway: puerta de entrada de las apps a los microservicios (fachada).
  • Escalabilidad Dinámica: se duplican o destruyen instancias según se van necesitando.
  • Ventajas:
  • Alta Escalabilidad: numerosas instancias del mismo componente y balanceo de carga entre ellas.
  • Agilidad: cada uno tiene un ciclo de desarrollo diferente.
  • Fácil Despliegue: son autosuficientes, sin dependencias externas o hardware específico.
  • Alta Testabilidad: fáciles de probar al completo.
  • Fácil Desarrollo: un nuevo desarrollador puede comprenderlo en poco tiempo.
  • Reusabilidad: es su razón de ser, que puedan ser reutilizados al máximo.
  • Interoperables: usan estándares abiertos y ligeros para comunicarse, independiente de la tecnología.
  • Inconvenientes:
  • Rendimiento: bastante latencia por comunicarse entre ellos vía red.
  • Múltiples Punto de Fallo: hay que gestionar bien los posibles fallos de comunicación.
  • Trazabilidad: al funcionar de forma asíncrona, tienen logs independientes que hay que unificar luego.
  • Transacciones: como cada componente tiene su BD, tendrían que ser distribuidas y complejas.
  • Desarrolladores Senior: requiere de programadores con buenos conocimientos y mucha experiencia.
  • Cuándo Usarlo:
  • Los proyectos deben nacer como monolíticos y pasarlos luego a microservicios cuando sea necesario.
  • No usar microservicios si el rendimiento es importante.
  • Usan sobre todo cuando:
  • La aplicación necesite de escalamiento dinámico.
  • La aplicación es muy grande y complicada, y hay grandes equipos de desarrollo.
  • Se quiere agilidad de desarrollo y se pueda desplegar cada componente de forma separada.

EDA (Event Driven Architecture)

  • Características:
  • Es asíncrona y distribuida, y está pensada para aplicaciones altamente escalables.
  • La comunicación entre componentes se realiza lanzando eventos.
  • Event Bus: administrador de todos los eventos, recibiéndolos y colocándolos en los event channels. También almacena los datos de forma temporal hasta que el componente destino esté disponible.
  • Existen diferentes topologías:
  • Mediador:
  • Procesa eventos complejos con una serie de pasos definidos y en orden (Orquestación).
  • Divide cada evento en otros más pequeños.
  • No tiene lógica de negocio ni transacciones.
  • Solo organiza los eventos para llevar un orden de ejecución.
  • Broker:
  • Procesa eventos simples que no requieren de orquestación.
  • Tan sólo se encarga de colocar los mensajes en las colas que corresponda.
  • Event Channel: colas (queue) o temas (topics) en los que habrá componentes a la escucha.
  • Event Processing: componentes a la escucha de que ocurran ciertos eventos. Llevan a cabo las acciones y pueden terminar de forma silenciosa o lanzar nuevos eventos que envían a su channel.
  • Cada componente es independiente, con su BD, siendo difícil llevar a cabo transacciones distribuidas.
  • MOM: middleware orientado a mensajes, especializados en transporte confiable de mensajes.
  • Se suelen usar dentro de arquitecturas SOA o de microservicios.
  • Ventajas:
  • Escalabilidad: cada consumidor escala de forma independiente y desacoplada.
  • Despliegue: la única dependencia que existe son los eventos (mucho desacople).
  • Rendimiento: permite el procesamiento en paralelo.
  • Flexibilidad: permite reaccionar rápidamente a un entorno cambiante.
  • Inconvenientes:
  • Testabilidad: pruebas muy sofisticadas debido a la asincronía.
  • Desarrollo: el desarrollo asíncrono, en especial la gestión de errores, es más complicado.
  • Complejidad: es la arquitectura más compleja de desarrollar y mantener.
  • Cuándo Usarlo:
  • La aplicación debe ser altamente escalable, con mucho volumen de transacciones.
  • Se requiere aprovechar el procesamiento paralelo, concurrente y asíncrono.
  • No son necesarias las respuestas síncronas.
  • Es necesario manejar eventos en tiempo real.

REST API Sync

REST API Async

HTTP Push

Queue

Rendimiento

Rápido

Muy rápido

Algo lento

Relativamente lento

Tamaño de Mensaje

Límites HTTP

Límites HTTP

Limitado

Ilimitado

Ejecución

Petición / Respuesta

Petición / Respuesta

WebSocket / Polling

Polling

Respuesta

Inmediata (síncrona)

Diferida (asíncrona)

Sin Respuesta

Sin Respuesta

Confiable

Suficiente

Algo confiable

No confiable

Muy confiable

Complejidad

Muy fácil

Fácil

Fácil

Difícil

Utilidad

Web apps

Web apps

Chat, monitoreo

Altos requerimientos

REST (Representational State Transfer)

  • Características:
  • Datos:
  • Los expone de forma estructurada (JSON), cada cliente los procesa como necesite.
  • Recurso: cualquier objeto referenciado por una URL de forma única.
  • Representaciones: diferentes formatos y metadatos para un mismo recurso.
  • Conectores:
  • Interfaces abstractas para acceder y transferir los recursos.
  • Oculta la implementación y mecanismos de comunicación subyacentes.
  • Todas las solicitudes a conectores son sin estado.
  • Cliente: inicia la comunicación, llevando a cabo una solicitud de un recurso.
  • Servidor: está a la escucha de peticiones de recursos, contestando a las mismas.
  • Caché: reutiliza respuestas a peticiones de recursos que no han cambiado.
  • Componentes: clientes de conectores (navegadores web, servidores web, etc.)
  • RESTful: servicios web REST mediante HTTP (GET, POST, PUT, PATCH, DELETE).
  • Ventajas:
  • Interoperable: se puede usar cualquier lenguaje de programación o tecnología que lo permita.
  • Escalabilidad: al usar solicitudes sin estado se pueden agregar nuevos nodos y balancear la carga.
  • Bajo Acoplamiento: al usar interfaces abstractas y URLs.
  • Testabilidad: se puede probar completamente cada recurso de forma individual.
  • Reutilización: al devolver los datos de forma estructurada y sin procesar.
  • Inconvenientes:
  • Rendimiento: existen latencia por la red, la autenticación y ser distribuido.
  • Más Puntos de Fallo: hay que gestionar bien los posibles fallos de comunicación.
  • Gobierno: una buena gestión y organización a la hora de desarrollarlos y mantenerlos.
  • Cuándo Usarlo:
  • Prácticamente todas las APIs están pasando de SOAP a REST.
  • Comunicaciones entre aplicaciones de distintas empresas.
  • Se suele usar en combinación con los microservicios.

N-Capas

Microkernel

SOA

Microservicios

Rendimiento

Regular

Regular

Bueno

Malo

Modificable

Fácil

Difícil

Difícil

Regular

Testable

Fácil

Fácil

Difícil (async)

Fácil

Escalable

Difícil

Difícil

Fácil

Fácil

Ágil

Difícil

Fácil

Fácil

Fácil

Desplegable

Difícil

Fácil

Regular

Regular

Patrones Arquitectónicos

DTO (Data Transfer Object)

  • Problemática:
  • Comunicación entre capas/servicios sin usar los objetos de entidades.
  • Solución:
  • Usar objetos ligeros con únicamente la información necesaria para esa acción.
  • Sólo tienen atributos, no métodos, y los datos pueden proceder de diversas fuentes.
  • Patrón Converter (Mapper):
  • Clases que centralizan la conversión desde entidades a DTOs y viceversa.
  • Heredan de una clase base abstracta (que actúa de interfaz).
  • Debe tener métodos para manejar tanto objetos individuales como listas de objetos.

DAO (Data Access Object)

  • Problemática:
  • Encapsular la lógica del acceso a datos, separándola de la lógica de negocio.
  • Solución:
  • Clases que centralizan el CRUD de las entidades.
  • Implementan la misma interfaz o hereda de una clase base abstracta.
  • Patrón Abstract Factory:
  • Desacopla el DAO a usar, para que negocio no tenga que saberlo.
  • Las “familias” serán los diferentes tipos de datos (SQL, XML, etc.)
  • Cada entidad implementa la misma interfaz en cada familia (IUserDao, IAddressDao, etc.)
  • Se crea un factory por cada familia (SqlDaoFactory, XmlDaoFactory, etc.) que implementan una interfaz común de factorías (IDaoFactory).

Polling

  • Problemática:
  • Enterarse de eventos acontecidos en sistemas externos.
  • Solución:
  • Servicio en segundo plano (Requestor) que realiza consultas (al Provider) cada cierto tiempo.
  • Puede ocasionar problemas de rendimiento, usar sólo si no hay otra alternativa.
  • Una alternativa más eficiente son los Webhooks.
  • Se debe llevar contabilidad de cambios, para no notificar dos veces del mismo.
  • Se usa mucho en monitoreo de servicios.

Webhook

  • Problemática:
  • Que un sistema externo notifique de los cambios sin tener que hacer polling.
  • Solución:
  • Los Requesters se suscriben a los eventos del Provider, que define tanto los eventos como sus datos.
  • Los Requesters informan al Provider de las URLs a invocar cuando ocurra un evento.
  • 2 formas de implementarse:
  • Enviar toda la información relevante en el propio evento (más inseguro, inyectar falsos eventos).
  • Enviar una notificación simple indicando que ocurrió y dejar que el cliente solicite más datos.
  • Reintentos: en caso de que el suscriptor esté temporalmente inactivo (polling en BD de eventos pdtes.)

Load Balance

  • Problemática:
  • Poder atender más peticiones sin escalar verticalmente el servidor.
  • Solución:
  • Escalar Horizontalmente: crear un clusters de servidores iguales que se repartan el mismo trabajo.
  • Balanceo de Carga:
  • de Cliente: el cliente elige un servidor del cluster (ineficiente, evitar).
  • de Servidor: el cliente solo se comunica con un balanceador de carga, que es el que se encarga de repartir el trabajo entre los distintos servidores del cluster.
  • Algoritmos de balanceo de carga:
  • Round-Robin: reparte la carga por igual entre todos los servidores (el más usado).
  • Todos los servidores son iguales.
  • Todos los procesos a realizar son iguales.
  • Weighted Round-Robin: a cada servidor se le asigna una ponderación estática que dependerá de sus capacidades de procesamiento.
  • Hay servidores más potentes que otros.
  • Todos los procesos a realizar son iguales.
  • Least Connected: se usa el servidor que tenga menos conexiones en cada momento.
  • Todos los servidores son iguales.
  • Hay procesos a realizar más complejos que otros.
  • Weighted Least Connected: cada servidor tiene una ponderación estática según capacidades.
  • Hay servidores más potentes que otros.
  • Hay procesos a realizar más complejos que otros.
  • Chained Failover: se envía las peticiones siempre al primer servidor que, si se satura o se cae, son reenviadas al segundo, y así sucesivamente.
  • Se usan en alta disponibilidad frente a fallos.
  • Weighted Response Time: se basa en los tiempos de respuesta de cada servidor para asignar procesos de forma dinámica.
  • El más rápido recibe más procesos.
  • Hash: cada recurso se asigna a un servidor y los procesos son asignados según el recurso que vayan a usar.
  • Se usa cuando se tiene conexiones con estados o cachés.
  • No se reparte la carga de forma equitativa, pudiendo saturar unos e infrautilizar otros.
  • Random: se elige el servidor de forma aleatoria.
  • Evitarla salvo que se tenga una buena razón.
  • Productos: F5, Nginx, HAProxy, LoadMaster, Eureka Server.

Service Registry

  • Problemática:
  • Saber qué servicios existen y en qué IPs y puertos están configurados, pero de forma dinámica.
  • Solución:
  • Servidor centralizado donde se registra cada servicio (o instancia de servicio) cuando es lanzado.
  • Cada servicio hace una petición periódica (heartbeat) para indicar que siguen activos.
  • Productos: Eureka Server.

Service Discovery

  • Problemática:
  • Poder usar un servicio sin tener que conocer su ubicación o hacer uso de archivos de configuración.
  • Solución:
  • Servidor centralizado a donde se solicitan los servicios, pudiendo hacer incluso balanceo de carga.
  • Se apoya en el Service Registry para realizar su labor.
  • Se accede a los servicios por un nombre, no hace falta conocer su ubicación real.
  • Del lado del Cliente: se encarga de llevar la lista de servicios y usarlos según necesite.
  • Ventajas: puede seguir funcionando aunque el Service Discovery se caiga.
  • Desventajas: se debe desarrollar para cada tecnología o framework específico.
  • Del lado del Servidor: hay un servidor para el Service Discovery.
  • Ventajas: el cliente no desarrolla nada, y permite balanceo de carga.
  • Desventajas: si se cae, los clientes no podrán acceder a los servicios.
  • Productos: Eureka Server.

API Gateway

  • Problemática:
  • Tener un único punto de entrada para los microservicios (autenticación, seguridad, perfiles, logs, etc.)
  • Agregar datos de diferentes fuentes, personalizado para cada perfil o usuario.
  • Solución:
  • Microservicio que actúa de intermediario (facade, proxy) entre el backend interno y los frontend externos.
  • Se apoya en el resto de microservicios, el service registry, el service discovery, etc.
  • Independiza la arquitectura interna de los servicios a terceros externos.
  • Puede haber varios API gateways, especializado cada uno en un tipo de cliente.
  • Productos: Zuul.

Access Token

  • Problemática:
  • Poder autenticarse sin tener que pasar usuario y contraseña en cada petición.
  • Solución:
  • Usar tokens cifrados, que se enviarán junto con cada petición.
  • Una vez validado un usuario con usr/pwd, se le asigna un token.
  • El token se suele guardar en una cookie, en el LocalStorage o tan sólo se le muestra al usuario.
  • Los token suelen tener una fecha de caducidad.
  • JWT (JSON Web Token):
  • Tiene 3 secciones en Base64, separadas por puntos:
  • Headers: el tipo de token y el algoritmo de cifrado usado.
  • Payload: datos identificativos del usuario (ID, nombre, rol, perfil, etc.)
  • Firma: hash del header y payload cifrado con contraseña que sólo conoce el servidor.

SSO (Single Sign On, Inicio de Sesión Único)

  • Problemática:
  • Centralizar la autenticación de todas las aplicaciones en un único sitio (solo un usr/pwd).
  • Solución:
  • Usar un mismo access token con todas las aplicaciones.
  • Si una aplicación ve que no viene token, lo redirecciona al SSO para que se autentique y obtenga uno.
  • El SSO también envía a la aplicación los datos identificativos del usuario.

Store and Forward (Queue)

  • Problemática:
  • Evitar la pérdida de mensajes que se envían entre sistemas a través de la red.
  • Causas: fallos de red, el destinatario está caído, hubo una excepción en el destinatario, etc.
  • Solución:
  • El mensaje se guarda en local y se envía sólo cuando el destinatario puede aceptarlo.
  • Se usa MOM (Message-Oriented Middleware) que es un SW especializado en gestión de mensajes.
  • El MOM sólo borrar el mensaje de su BD local cuando está seguro de que ha sido procesado.
  • El consumidor los procesa a su ritmo, sin que el productor lo sature con demasiadas peticiones.
  • Versión Avanzada: se usan 2 MOMs, uno de salida en el productor y otro de entrada en el consumidor.
  • Producto: RabbitMQ, Kafka.

Circuit Breaker

  • Problemática:
  • Reportar fallos y actuar en consecuencia de forma eficiente (tolerancia a fallos automática).
  • Que el sistema parezca funcionar bien hasta que se recupere, de forma automática.
  • Solución:
  • El consumo de servicios se hará a través de un circuit breaker (que es como un fusible).
  • El circuit breaker conocerá si el servicio está funcionando bien o no.
  • Si fallan varias peticiones seguidas (según configuración) se corta comunicación con el servicio.
  • Mientras el servicio esté caído, el circuit breaker informará de ello a los clientes al hacer peticiones.
  • Los clientes pueden intentarlo varias veces más (de forma automática) durante un tiempo.
  • Cada cierto tiempo, el circuit breaker comprobará si el servicio ya ha vuelto a estar activo.
  • Cuando vuelve a estar activo, las peticiones del cliente volverán a enviarse, sin problema, al servicio.
  • Estados:
  • Closed: el servicio está activo y todo funciona bien.
  • Open: el servicio se ha caído (varios fallos seguidos) y el circuit breaker lo ha detectado.
  • Half-Open: cuando vuelve a comprobar si el estado está ya activo de nuevo.
  • Producto: Hystrix.

Log Aggregation

  • Problemática:
  • Tener una traza completa de la ejecución cuando hay servicios distribuidos, cada uno con su log.
  • Solución:
  • Un componente externo centraliza todos los logs, normalmente en una BD propia.
  • Cada transacción se registra con un mismo ID por parte de todos los servicios.
  • No hace falta guardar toda la información de los logs locales, sólo lo necesario para la trazabilidad.
  • El formato de todos los logs se debe estandarizar antes de guardarlos en la BD.
  • Productos: Sleuth + Zipkin, ELK (beats → logstash → elasticsearch → kibana).

Recursos