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
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:
- Establecer los objetivos (junto a los directivos).
- Entender los requerimientos del sistema (junto al analista del sistema y jefe del proyecto).
- Entender los requerimientos no funcionales (atributos de calidad, guiar/preguntar al cliente).
- Crear mapa de componentes / servicios (demostrar al cliente que lo entiendes todo bien).
- Seleccionar la tecnología (para cada componente).
- Diseñar la arquitectura (todos los detalles, junto al equipo de desarrollo):
- Infraestructura.
- Plataforma.
- Módulos / Clases.
- Escribir la documentación.
- Dar soporte al equipo de desarrollo.
- Funcionales: hay que analizar los siguientes aspectos:
- Flujos del negocio.
- Servicios del negocio.
- Interfaces de usuario.
- No Funcionales: atributos de calidad, los más importantes tienen que ver con estos aspectos:
- Rendimiento: nº tareas por unidad de tiempo, tiempos normal y máximo de respuesta/ejecución.
- Carga: nº de tareas simultáneas sin colapsar el sistema.
- Volumen: de datos (bytes): tipo de almacén, tipo de queries, cantidad a almacenar (inicial y crecimiento).
- Concurrencia de usuarios (suele ser 10 veces la carga).
- SLA: porcentaje de tiempo funcionando (ej. 99,9%). [100% es imposible, hay que ser realistas]
- Puntos Clave:
- Rendimiento: latencia (CPU, RAM, disco, red, procesado), asincronía, concurrencia y caché. [carga fija]
- 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.
- Confianza: SLA, redundancia, monitoreo, recuperación (hot, warm, backup), tolerancia a fallos parciales.
- Seguridad: cifrado, firma, firewall, autenticación, autorización, tokens (SSO, JWT), roles, ataques.
- 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.
- Web: https://jwt.io
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).