01 - Arquitecturas de software

Podemos clasificar una aplicación atendiendo a distintos criterios. Estas decisiones se combinan: por ejemplo, una aplicación puede desplegarse como monolito modular y organizarse internamente por capas con inversión de dependencias.

Las arquitecturas limpias protegen las reglas de negocio frente a los detalles técnicos y externos.

  • Independencia de frameworks: el framework es una herramienta, no el centro de la aplicación.
  • Testeable: las reglas de negocio pueden probarse sin levantar la interfaz, la base de datos ni el framework.
  • Independencia de la interfaz de usuario: la misma lógica puede exponerse mediante interfaces distintas.
  • Independencia de la base de datos: se puede sustituir la tecnología de persistencia sin reescribir las reglas de negocio.
  • Independencia de agentes externos: las reglas de negocio no dependen de servicios externos concretos.
  • Regla de dependencia: las dependencias del código apuntan hacia las políticas y reglas de negocio, no hacia los detalles.
  • Inversión de dependencias (DIP): las abstracciones que necesita el núcleo pertenecen al núcleo; los detalles externos las implementan.

controllerdomainpersistenceControllerUseCaseModelRepositoryPortRepositoryAdapterBase de datosinvocaaplica reglasusa abstraccionimplementaaccede

Arquitectura limpia es un concepto general: busca proteger las reglas de negocio y dirigir las dependencias hacia el núcleo. La arquitectura hexagonal, la arquitectura de cebolla y la arquitectura por capas con inversión de dependencias (DIP) son distintos enfoques de arquitectura limpia.

Organiza el código según responsabilidades. En aplicaciones web es habitual distinguir estas capas:

Capa Responsabilidad
controller Recibir solicitudes, coordinar la entrada y producir respuestas
domain Contener servicios, casos de uso y reglas del negocio
persistence Implementar el acceso a datos y comunicarse con la base de datos

En una arquitectura por capas tradicional, controller llama a domain y este utiliza persistence. Si el dominio depende directamente de clases concretas de persistencia, queda acoplado a la tecnología.

Cuando domain define las interfaces que necesita y persistence las implementa, se invierte la dependencia respecto a la persistencia y se aplican principios de arquitectura limpia.

Las capas son una división lógica, no una estructura rígida. Se pueden añadir capas o separar componentes según las necesidades; por ejemplo, los casos de uso pueden estar separados de los servicios de dominio.

controllerdomainpersistenceControllerServiceModelRepositoryPortRepositoryAdapterBase de datosusaimplementa

Separa el núcleo de la aplicación de los sistemas externos mediante puertos y adaptadores.

  • Puerto: abstracción que describe cómo se usa el núcleo o qué necesita del exterior. Por ejemplo, una interfaz de repositorio.
  • Adaptador: conecta un puerto con una tecnología concreta. Por ejemplo, un controller REST o una implementación JPA.
  • Dirección de dependencia: el adaptador implementa o invoca el puerto; el dominio no conoce el adaptador.

Adaptadores de entradadomainpersistenceAdaptador de servicio externoControllerUseCaseModelRepositoryPortNotificationPortRepositoryAdapterEmailAdapterBase de datosServicio de correoinvocaaplica reglasusa puertoimplementausa puertoimplementa

Enfoque Idea principal Observación
Capas tradicional Agrupar por responsabilidad y delegar entre capas Sencillo; vigilar el acoplamiento hacia infraestructura
Hexagonal Aislar el núcleo mediante puertos y adaptadores Facilita sustituir tecnologías y probar el núcleo
Cebolla Organizar el sistema en anillos alrededor del dominio Las dependencias apuntan hacia el centro

Describe cómo se empaqueta, ejecuta y despliega la aplicación. Es un criterio distinto de la organización interna.

Estilo Despliegue Límites y datos Ventajas y costes
Monolito Una aplicación desplegable Los componentes suelen compartir proceso y almacenamiento Operación sencilla; puede crecer con alto acoplamiento
Monolito modular Una aplicación desplegable Módulos con responsabilidades, datos encapsulados y APIs internas explícitas Mantiene simplicidad operativa y mejora la modularidad
Microservicios Varios servicios desplegables Cada servicio controla sus datos; se comunican por red Despliegue y escalado independientes; más complejidad operativa
  • Monolito no significa necesariamente código desordenado: puede tener módulos bien diseñados. Es una opción adecuada para aplicaciones pequeñas y poco complejas, como un CRUD sencillo.
  • Monolito modular: los módulos colaboran dentro de un mismo proceso mediante APIs explícitas. Las fronteras limitan dependencias y acceso a datos internos.
  • Microservicio: servicio autónomo que se despliega por separado y es responsable de sus datos. No basta con separar una aplicación en varios proyectos.
No hay que confundir cómo se organiza el código (por capas, hexagonal, cebolla) con cómo se despliega (monolito, monolito modular, microservicios). Un monolito puede aplicar principios de arquitectura limpia.

Monolito: un despliegueMonolito modular: un despliegueAplicacionMicroservicios: varios desplieguesAplicacion(modulos juntos)Base de datosBase de datos compartida(datos propiedad de cada modulo)Modulo A(API publica)Modulo B(API publica)Servicio AServicio BServicio CBD ABD BBD Csolo sus datossolo sus datosAPI internaHTTP / mensaje

Microservicios no son simplemente módulos separados en carpetas o proyectos: cada servicio debe poder desplegarse y evolucionar de forma autónoma, y la comunicación pasa por la red.

La comunicación entre componentes puede ser síncrona o asíncrona. La forma concreta depende de si los componentes están dentro del mismo proceso o son servicios distribuidos.

Tipo Funcionamiento Características
Síncrona El emisor hace una llamada y espera una respuesta Directa; el emisor depende de la disponibilidad y el tiempo de respuesta del receptor
Asíncrona El emisor publica un evento o mensaje y no espera a que se complete su procesamiento Desacopla temporalmente; puede haber retrasos, reintentos y entregas duplicadas

Comunicación entre módulos

  • Los módulos de una misma aplicación pueden comunicarse de forma síncrona, mediante llamadas directas a las APIs públicas de otros módulos.
  • También pueden comunicarse de forma asíncrona, publicando eventos que otros módulos consumen.
  • Los eventos internos no implican comunicación de red ni crean por sí solos aislamiento o una transacción independiente.

Comunicación entre microservicios

  • Los microservicios se comunican a través de la red.
  • La comunicación síncrona suele realizarse mediante peticiones HTTP y respuestas.
  • La comunicación asíncrona se realiza publicando mensajes o eventos en un intermediario (broker), como RabbitMQ o Kafka; los consumidores los procesan posteriormente.
  • En cualquiera de los estilos distribuidos, hay que contemplar fallos de red. Con mensajería, los consumidores deben ser idempotentes para tolerar entregas duplicadas.

ClienteServicio AServicio BBroker de mensajesConsumidorClienteServicio AServicio BBroker de mensajesConsumidorClienteServicio AServicio BBroker de mensajesConsumidorComunicacion sincrona entre modulos (mismo proceso)API publica (llamada directa)resultadoComunicacion sincrona entre microservicios (HTTP)solicitudpetición HTTPrespuestaresultadoComunicacion asincrona entre componentes (eventos)publicar eventoentregar eventoconfirmar procesamiento

La mensajería desacopla al emisor del momento en que se procesa el evento, pero no garantiza que cada mensaje se procese exactamente una vez. Diseña consumidores idempotentes.
Durante el curso estudiaremos las capas y sus componentes en temas separados. Como referencia, seguiremos una arquitectura por capas desplegada como monolito modular.
  • clase/daw/dws/1eval/arquitecturas.txt
  • Última modificación: 2026/10/02 10:48
  • por cesguiro