====== 08 - Arquitectura de aplicación ======
Hasta aquí, cada servidor se ha arrancado por su lado: un Tomcat, un MinIO, una base de datos. Una aplicación real es un **sistema** de varios servidores que colaboran. **Docker Compose** (tema 02) permite describir ese sistema completo en un ''compose.yaml'' y arrancarlo de una vez.
===== 1. El sistema =====
La arquitectura de referencia de una aplicación web clásica:
@startuml
actor "Navegador" as Browser
node "nginx\npuerto 80" as Nginx
node "App\nTomcat :8080" as App
database "MariaDB\n:3306" as Db
node "MinIO\n:9000" as Minio
Browser --> Nginx : HTTP (único puerto publicado)
Nginx --> Browser : frontend estático
Nginx --> App : /api/** (reverse proxy)
App --> Db : JDBC (red interna)
App --> Minio : API S3 (red interna)
@enduml
* **nginx** es el punto de entrada único: sirve el **frontend** estático (HTML, CSS, JS) y actúa de **reverse proxy**: reenvía las rutas de la API a la aplicación.
* **App** (Tomcat): la **API REST** en Java; no publica puerto propio, solo responde al proxy.
* **MariaDB**: la persistencia relacional.
* **MinIO**: el **almacenamiento de objetos** para los ficheros.
===== 2. La descripción: compose.yaml =====
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./frontend:/usr/share/nginx/html:ro
depends_on:
- app
app:
build: ./app
environment:
DB_URL: jdbc:mariadb://db:3306/miapp
S3_ENDPOINT: http://minio:9000
depends_on:
- db
- minio
db:
image: mariadb:11
environment:
MARIADB_ROOT_PASSWORD: secreto
MARIADB_DATABASE: miapp
volumes:
- db-data:/var/lib/mysql
minio:
image: elestio/minio
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: secreto123
volumes:
- minio-data:/data
volumes:
db-data:
minio-data:
$ docker compose up -d # los cuatro servicios, juntos
$ docker compose down
===== 3. Red interna y nombres =====
Compose crea una **red propia** donde cada servicio es alcanzable por su **nombre**: Docker resuelve ''db'' y ''minio'' a la IP de esos contenedores (DNS interno).
* En ''DB_URL'' la base de datos es ''db:3306'', no ''localhost'': el contenedor de la app no es el mismo que el de la BBDD.
* Lo mismo con ''S3_ENDPOINT: http://minio:9000''.
* Solo **nginx** publica puerto al exterior (''80:80''): la app, la BBDD y MinIO viven en la red interna y no son accesibles desde el host. La superficie de exposición es mínima y coincide con la de un despliegue real.
''depends_on'' ordena el arranque de los servicios.
===== 4. La configuración de nginx =====
server {
listen 80;
location /api/ {
proxy_pass http://app:8080;
}
location / {
root /usr/share/nginx/html;
}
}
Para el navegador todo sale del mismo servidor: el frontend en ''/'' y la API en ''/api/''. El proxy usa el nombre ''app'' de la red interna; si la app cambia de nombre o de puerto, solo toca este fichero.
===== 5. Lo que aporta =====
^ Aporte ^ Cómo lo da Compose ^
| Reproducibilidad | Un ''compose.yaml'' describe el sistema completo: el mismo resultado en cualquier máquina |
| Paralelismo con producción | La topología (proxy + app + BBDD + storage) es la de un despliegue real |
| Aislamiento | Cada pieza en su contenedor, con su versión exacta |
| Exposición mínima | Solo nginx publica un puerto; el resto vive en la red interna |
La arquitectura del ejemplo es la clásica de tres niveles: presentación (nginx), aplicación (Tomcat) y datos (MariaDB, MinIO). Sustituir una pieza —otra base de datos, **S3** real en producción— toca la configuración del servicio, no la estructura del sistema. Y si la aplicación necesita más capacidad, se escala horizontalmente añadiendo réplicas detrás del proxy.