3.3.-Estructura de una aplicación web
3.3. Estructura de una aplicación web¶
Idea principal
Antes de desplegar una aplicación hay que saber qué hemos recibido, qué proceso la ejecuta, qué dependencias necesita, qué configuración cambia y qué datos deben sobrevivir. Un inventario razonado evita tratar una web estática, una aplicación PHP, un proyecto Node.js y un WAR de Java como si fueran el mismo tipo de entrega.
Este punto concreta la arquitectura estudiada en el apartado 3.1 y utiliza la comunicación HTTP del apartado 3.2 para seguir los recursos que intervienen en una petición. Su finalidad es analizar la aplicación y sus recursos antes de decidir cómo se instalará el entorno. Los requisitos de CPU, memoria, sistema operativo y red se desarrollarán en el punto 3.4, Requisitos de implantación.
La normativa del módulo profesional 0614 relaciona este contenido con:
| Código | Descripción |
|---|---|
| RA1 | Implanta arquitecturas web analizando y aplicando criterios de funcionalidad. |
| CE g | Se ha analizado la estructura y recursos que componen una aplicación web. |
Qué deberías saber al terminar
Al acabar este tema deberías poder:
- distinguir código fuente, artefacto, configuración y datos en un proyecto;
- reconocer los directorios y archivos relevantes de aplicaciones web habituales;
- identificar las dependencias de construcción y de ejecución;
- localizar entradas, puertos, integraciones y datos persistentes;
- elaborar un inventario que justifique qué se despliega y qué se mantiene fuera de la aplicación.
Mapa del tema
Seguiremos esta secuencia:
- del código fuente a la aplicación en ejecución;
- clasificación de los recursos;
- lectura de estructuras habituales y artefactos;
- relación entre componentes en tiempo de ejecución;
- inspección e inventario antes del despliegue.
1. Del código fuente a la aplicación en ejecución¶
Analizar una aplicación no consiste en abrir todos sus archivos sin criterio. Consiste en encontrar evidencias que permitan responder preguntas concretas:
- ¿Qué proceso recibe una petición y cuál es su punto de entrada?
- ¿Qué archivos se sirven directamente y cuáles se interpretan o compilan?
- ¿Qué dependencias se necesitan para construir y para ejecutar la aplicación?
- ¿Qué valores cambian entre desarrollo, pruebas y producción?
- ¿Qué información debe conservarse al reiniciar o recrear un proceso?
- ¿Cuál es el artefacto que se ha de instalar, copiar o ejecutar?
Código fuente, artefacto y ejecución
El código fuente es el conjunto de archivos que el equipo modifica. Un
artefacto es una salida identificable del proceso de construcción,
preparada para distribuirse o desplegarse, como dist/, un archivo WAR o
una imagen Docker. La ejecución es el momento en que un servidor o un
runtime utiliza ese artefacto junto con su configuración y sus datos.
Estas tres cosas no son intercambiables. Por ejemplo, pom.xml describe cómo
construir un proyecto Java, target/catalogo.war es una salida construida y
Tomcat es el runtime que puede desplegar ese WAR. Del mismo modo, un
Dockerfile es una receta; la imagen construida es el artefacto y el contenedor
es una instancia en ejecución.
flowchart LR
S["Codigo fuente<br/>src, public, package.json"] --> B["Construccion<br/>dependencias y pruebas"]
B --> A["Artefacto<br/>dist, WAR o imagen"]
A --> R["Ejecucion<br/>servidor y configuracion"]
R --> D["Datos persistentes<br/>base de datos y ficheros"]
El esquema también explica qué no debe confundirse:
| Elemento | Qué representa | Ejemplos |
|---|---|---|
| Fuente | Lo que se edita y versiona | src/, public/, pom.xml |
| Salida de construcción | Resultado reproducible de una build | dist/, target/app.war |
| Runtime | Programa que ejecuta o sirve la aplicación | Nginx, PHP-FPM, Node.js, Tomcat |
| Descriptor de despliegue | Cómo se conectan servicios y recursos | compose.yaml, manifiesto, configuración de Nginx |
| Datos | Estado que la aplicación crea o consulta | Tablas, cargas de usuarios, ficheros subidos |
Un compose.yaml puede describir un entorno formado por varios servicios,
redes y volúmenes, pero no sustituye al código ni convierte los datos en parte
de la imagen. Esta distinción será esencial al trabajar con contenedores.
2. Clasificación de los recursos¶
Clasificar los recursos ayuda a saber quién los utiliza, cuándo cambian y qué protección necesitan. Un mismo proyecto puede contener recursos de varias categorías.
| Tipo de recurso | Función | Ejemplos | Tratamiento habitual |
|---|---|---|---|
| Código fuente | Define comportamiento y reglas | Controladores, servicios, server.js, index.php |
Se versiona y se construye o interpreta |
| Estático | Se entrega sin ejecutar lógica de negocio | HTML, CSS, JavaScript, imágenes, fuentes | Lo sirve el servidor web o una CDN |
| Dinámico | Se procesa en el servidor | PHP, servlets, controladores, plantillas | Necesita un runtime y, a menudo, datos |
| Configuración | Adapta la aplicación al entorno | YAML, XML, JSON, .env.example |
Se externaliza y se valida por entorno |
| Dependencias | Aporta bibliotecas o herramientas externas | pom.xml, package.json, composer.json |
Se fijan versiones y se instalan de forma reproducible |
| Persistente | Conserva estado entre reinicios | Base de datos, cargas y documentos | Vive en un volumen o servicio de almacenamiento |
| Transitorio | Se puede recrear o eliminar | Caché, temporales, logs de una instancia | Se gestiona fuera del artefacto o con una política clara |
| Distribución | Permite transportar y desplegar | WAR, dist/, imagen Docker |
Se etiqueta, verifica y publica |
2.1. Estático no significa necesariamente «escrito a mano»¶
Un archivo que el servidor entrega sin interpretarlo es estático en tiempo de
ejecución, aunque se haya generado durante la construcción. Por ejemplo,
app.scss es una fuente, mientras que dist/app.css es un recurso estático
generado. Esta diferencia permite localizar si un fallo está en la build o en
el servidor web.
2.2. Configuración, secretos y datos¶
La configuración contiene valores necesarios para ejecutar el servicio, como el puerto o el nombre del host de la base de datos. Los secretos —contraseñas, tokens y claves privadas— son una clase especialmente sensible de configuración y no deben guardarse en el repositorio ni dentro de una imagen.
No confundas plantilla con secreto
.env.example puede documentar los nombres de las variables necesarias y
valores de ejemplo no sensibles. El archivo .env real debe excluirse del
control de versiones y sus valores deben proceder de un mecanismo seguro de
configuración. Cambiar una variable no debería obligar a reconstruir la
imagen si el cambio solo pertenece al entorno de ejecución.
Un archivo como schema.sql describe la estructura inicial de una base de
datos; no es la base de datos en producción. Las filas creadas durante el uso
de la aplicación deben persistir en el servicio de datos o en un volumen.
2.3. Dependencias de construcción y de ejecución¶
No todas las dependencias tienen el mismo destino:
- una herramienta de compilación, como Maven o un bundler de JavaScript, puede ser necesaria solo durante la construcción;
- una biblioteca incluida en un WAR o en
node_modulespuede viajar con el artefacto; - un runtime, como PHP, la JVM o Node.js, debe estar disponible donde se ejecute la aplicación;
- un servicio externo, como una base de datos, no se incluye necesariamente en el artefacto, pero sí forma parte de sus dependencias operativas.
Los archivos de bloqueo, como package-lock.json, composer.lock o un
catálogo de versiones equivalente, ayudan a que dos construcciones instalen
las mismas versiones. package.json expresa el proyecto y sus rangos; el
archivo de bloqueo concreta el árbol resuelto para una instalación repetible.
3. Estructuras y artefactos habituales¶
No existe un único árbol universal. Para reconocer una aplicación hay que combinar el nombre de las carpetas con su gestor de dependencias, su documentación y su punto de entrada.
3.1. Proyecto Java con Maven y WAR¶
Una aplicación Java web podría recibirse con esta estructura:
catalogo-web/
├── README.md
├── LICENSE
├── CHANGELOG.md
├── .gitignore
├── .env.example
├── .dockerignore
├── docs/
│ ├── arquitectura.md
│ ├── instalacion.md
│ └── configuracion.md
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/ # Controladores, servicios y dominio
│ │ ├── resources/ # Configuración y plantillas
│ │ └── webapp/ # HTML, CSS, JS y WEB-INF
│ └── test/ # Pruebas automatizadas
├── db/
│ └── schema.sql # Inicialización, no datos de producción
├── compose.yaml # Entorno local de varios servicios
└── Dockerfile # Receta para construir una imagen
Después de una construcción correcta puede aparecer una salida como esta:
src/ y pom.xml son material de construcción; target/catalogo-web.war es
el artefacto que se puede copiar al directorio webapps de Tomcat. Los
directorios de resultados, informes y cachés no se deben confundir con la
estructura fuente ni copiar sin revisión a producción.
3.2. Anatomía mínima de un WAR¶
Un WAR es un archivo ZIP con una convención que Tomcat reconoce. Una estructura simplificada podría ser:
catalogo-web.war
├── index.html
├── css/
├── js/
├── WEB-INF/
│ ├── web.xml
│ ├── classes/ # Clases compiladas
│ └── lib/ # Bibliotecas del runtime
└── META-INF/
WEB-INF/ no se sirve directamente al navegador. Su contenido lo utiliza el
servidor de aplicaciones para configurar y ejecutar la aplicación. El nombre
del WAR suele determinar el contexto, por ejemplo catalogo-web.war puede
publicarse como /catalogo-web, salvo que la configuración del servidor indique
otro contexto.
3.3. Estructuras de Node.js, PHP y una web estática¶
| Tecnología | Archivos o directorios que conviene localizar | Salida o runtime habitual |
|---|---|---|
| Node.js | package.json, archivo de bloqueo, src/, public/, server.js |
dist/ para frontend o proceso Node para backend |
| PHP | composer.json, composer.lock, public/, vendor/, .env.example |
Código PHP servido por Apache o PHP-FPM |
| Web estática | index.html, css/, js/, img/ o dist/ |
Directorio servido por Nginx, Apache o una CDN |
| Java web | pom.xml o build.gradle, src/, WEB-INF/ |
WAR desplegado en Tomcat u otro servidor |
En PHP, vendor/ contiene dependencias instaladas por Composer y no debe
regenerarse de forma arbitraria en producción si el despliegue exige una
instalación reproducible. En Node.js, npm ci utiliza el archivo de bloqueo y
está pensado para instalaciones limpias; el comando exacto depende del proyecto
y se debe comprobar en package.json.
3.4. Imágenes, contenedores, redes y volúmenes¶
Cuando la aplicación se distribuye como imagen Docker, el artefacto contiene el runtime y los archivos necesarios para ejecutar el servicio. El contenedor es una instancia de esa imagen; la red permite la comunicación entre servicios y el volumen conserva datos fuera del ciclo de vida del contenedor.
Por ejemplo, en una aplicación PHP con MariaDB:
- la imagen de la aplicación contiene PHP, sus extensiones y el código;
- el contenedor de MariaDB ejecuta otro servicio y mantiene sus datos en un volumen;
- las variables de entorno indican a PHP cómo localizar la base de datos;
- la red interna permite usar el nombre del servicio como host sin publicar el puerto de MariaDB al exterior.
En una aplicación Java, la alternativa puede ser una imagen que contenga
Tomcat y copie el WAR a webapps. En ambos casos, el contenido de la base de
datos y las cargas de usuarios no deberían formar parte de la imagen de la
aplicación.
4. Del árbol de archivos al mapa de ejecución¶
El árbol de directorios muestra cómo está organizado el proyecto, pero no explica por sí solo qué ocurre cuando llega una petición. El inventario debe relacionar cada recurso con el proceso que lo utiliza.
flowchart LR
U["Navegador"] -->|"HTTP o HTTPS"| W["Servidor web<br/>Nginx o Apache"]
W -->|"Estático"| U
W -->|"Dinámico"| A["Runtime de la aplicacion<br/>PHP, Node o Tomcat"]
A -->|"Consulta o actualizacion"| DB["Base de datos"]
A -->|"Lectura y escritura"| F["Almacenamiento persistente"]
C["Configuracion y secretos"] --> A
En este mapa se pueden identificar responsabilidades distintas:
| Componente | Recursos que utiliza | Pregunta de inspección |
|---|---|---|
| Servidor web | Sitio, rutas, certificados y recursos estáticos | ¿Qué archivos sirve y a dónde reenvía lo dinámico? |
| Runtime | Código, bibliotecas y variables de entorno | ¿Qué comando o proceso inicia la aplicación? |
| Base de datos | Esquema, migraciones y datos de trabajo | ¿Dónde están el host, el puerto y las credenciales? |
| Almacenamiento | Cargas, documentos y logs seleccionados | ¿Qué sobrevive al recrear el proceso? |
| Red | Puertos publicados y redes internas | ¿Qué componentes deben comunicarse y cuáles no deben exponerse? |
Un Dockerfile puede instalar dependencias y copiar el código, pero no es la
aplicación en ejecución. Del mismo modo, compose.yaml puede conectar web,
app y db, pero no contiene por sí solo las reglas de negocio de app.
Caso de referencia: catálogo web
Una petición a /productos/42 puede llegar a Nginx. Si solicita una hoja
CSS, Nginx la sirve directamente. Si solicita el producto, reenvía la
petición al runtime; este usa sus bibliotecas, consulta la base de datos y
devuelve HTML o JSON. La ficha de inventario debe poder señalar qué archivo,
proceso, configuración y servicio de datos intervienen en cada paso.
5. Cómo inspeccionar una aplicación antes de desplegarla¶
La inspección debe ser ordenada y dejar evidencias. Una secuencia útil es la siguiente.
5.1. Leer la documentación y localizar la entrada¶
Empieza por README.md, la documentación de instalación y los scripts del
proyecto. Busca el comando de inicio, el puerto, el contexto de despliegue y
los servicios externos. No supongas que server.js, index.php o target/*.war
son la entrada correcta sin comprobarlo.
Para una primera inspección sin ejecutar el proyecto se pueden utilizar órdenes de solo lectura:
find . -maxdepth 3 -type f | sort
grep -RInE "(PORT|HOST|DATABASE|DB_|npm start|mvn package)" \
README.md docs package.json pom.xml 2>/dev/null
La segunda orden es orientativa: no todos los archivos existen en todos los proyectos y nunca debe utilizarse para imprimir valores secretos.
5.2. Revisar dependencias y resultados de construcción¶
Localiza el gestor que corresponda y anota sus archivos:
| Evidencia | Qué permite conocer |
|---|---|
pom.xml o build.gradle |
Versión de Java, plugins y bibliotecas declaradas |
package.json y archivo de bloqueo |
Scripts, runtime de Node y versiones resueltas |
composer.json y composer.lock |
Versión de PHP y paquetes de Composer |
requirements.txt o pyproject.toml |
Dependencias y metadatos de Python |
Dockerfile |
Imagen base, usuario, puertos, copias y comando de inicio |
compose.yaml |
Servicios, redes, volúmenes y variables del entorno |
Si ya existe un artefacto, inspecciónalo sin ejecutarlo. Por ejemplo, para conocer el contenido de un WAR:
La salida permite comprobar si contiene WEB-INF/, clases, bibliotecas y
recursos web. Si falta una biblioteca o una plantilla, el problema pertenece a
la construcción del artefacto, no a la configuración de Nginx.
5.3. Separar configuración, secretos y valores por entorno¶
Anota los nombres de las variables, no sus valores reales:
APP_PORT=8080
DB_HOST=db
DB_NAME=catalogo
DB_USER=app_user
DB_PASSWORD= # Se proporciona fuera del repositorio
La configuración debe indicar qué valor necesita la aplicación y qué componente
lo consume. También hay que saber si el valor se lee al construir la imagen o
al iniciar el contenedor: incluir una contraseña en una instrucción RUN o en
una capa de imagen puede dejarla recuperable incluso después de borrar el
archivo original.
5.4. Identificar persistencia e integraciones¶
Busca migraciones, scripts de inicialización, rutas de subida, colas, cachés y clientes de servicios externos. Para cada uno, registra:
- qué datos produce o necesita;
- dónde se almacenan;
- qué sucede al reiniciar el proceso;
- cómo se configura la conexión;
- qué evidencia demuestra que funciona.
No basta con encontrar db/schema.sql: hay que comprobar dónde se ejecuta, si
es idempotente y dónde quedan los datos después de crear las tablas.
6. Inventario de recepción y decisiones de despliegue¶
El resultado de la inspección puede resumirse en una ficha. Esta ficha evita que el despliegue dependa de recordar comandos sueltos.
| Elemento | Pregunta que se responde | Evidencia que se debe guardar |
|---|---|---|
| Entrada | ¿Qué proceso inicia la aplicación? | README, Dockerfile, pom.xml o package.json |
| Artefacto | ¿Qué se distribuye? | dist/, WAR, paquete o digest de imagen |
| Runtime | ¿Qué ejecuta el artefacto? | Versión de Node, PHP, JVM, Tomcat u otro servidor |
| Puerto y ruta | ¿Dónde escucha y qué contexto usa? | Configuración, EXPOSE, servidor web o documentación |
| Dependencias | ¿Qué bibliotecas y versiones necesita? | Archivos de dependencias y bloqueo |
| Configuración | ¿Qué cambia entre entornos? | .env.example, variables documentadas y configuración |
| Persistencia | ¿Qué datos deben conservarse? | Volúmenes, tablas, rutas de carga y política de copias |
| Integraciones | ¿Qué servicios externos utiliza? | Hosts, puertos, contratos y permisos |
| Comprobación | ¿Cómo se verifica que funciona? | Prueba, comando, resultado, fecha y logs relevantes |
Una ficha cumplimentada para el catálogo podría ser:
| Campo | Decisión documentada |
|---|---|
| Artefacto | catalogo-web.war construido con Maven |
| Runtime | Tomcat, con Java en la versión indicada por el proyecto |
| Entrada | Contexto /catalogo-web y controlador de productos |
| Datos | MariaDB en un servicio separado y red interna |
| Persistencia | Volumen para los datos de MariaDB; no se guardan en el WAR |
| Configuración | Host, puerto y credenciales suministrados al entorno |
| Evidencia | jar tf, logs de Tomcat, petición GET y consulta de salud |
Esta ficha no sustituye a la guía de instalación. Su función es demostrar que se ha entendido la estructura y que cada decisión se apoya en un archivo, una configuración o una prueba observable.
7. Buenas prácticas¶
Para trabajar este contenido con rigor conviene aplicar estas prácticas:
- Inspeccionar antes de ejecutar: lee scripts, permisos, rutas y variables antes de lanzarlos con privilegios.
- Distinguir fuente, salida y runtime: documenta qué se edita, qué se construye y qué se instala o ejecuta.
- Fijar versiones reproducibles: conserva archivos de bloqueo, versiones de runtimes y la etiqueta o el digest del artefacto probado.
- Externalizar la configuración: cambia puertos, hosts y opciones sin reconstruir el artefacto cuando pertenezcan al entorno.
- Proteger los secretos: utiliza un mecanismo de secretos y no los copies a Git, logs, imágenes ni capturas.
- Separar código y datos: la imagen o el WAR pueden recrearse; los datos de trabajo deben tener almacenamiento persistente y copias verificadas.
- Reducir la exposición: publica solo el punto de entrada necesario y deja la base de datos en una red interna.
- Verificar el contenido del artefacto: comprueba archivos, permisos, dependencias y procedencia antes de instalarlo.
- Relacionar decisiones con evidencias: anota el comando o prueba, el resultado obtenido y la fecha de comprobación.
8. Errores frecuentes¶
| Error frecuente | Por qué ocurre | Cómo evitarlo |
|---|---|---|
| Copiar el repositorio entero al servidor | Se confunde fuente con entrega | Identifica y verifica el artefacto que corresponde al runtime |
Confundir Dockerfile, imagen y contenedor |
Se mezclan receta, paquete e instancia | Explica qué ocurre en build y qué ocurre al iniciar |
| Guardar la base de datos dentro de la imagen | Se ignora el ciclo efímero del contenedor | Separa el servicio de datos y usa almacenamiento persistente |
Tratar schema.sql como datos de producción |
Se confunde inicialización con estado de trabajo | Ejecuta migraciones controladas y conserva los datos fuera del artefacto |
| Instalar dependencias sin archivo de bloqueo | Cada entorno resuelve versiones distintas | Versiona y utiliza el mecanismo reproducible del gestor |
Publicar .env o una contraseña en el Dockerfile |
Se prioriza la rapidez sobre la seguridad | Usa .env.example y un gestor de secretos |
| Suponer que una carpeta indica la tecnología | Muchos proyectos comparten nombres parecidos | Comprueba documentación, punto de entrada y gestor de dependencias |
| Ejecutar scripts no revisados como administrador | Se confía en el nombre del archivo | Inspecciona comandos, permisos, rutas y variables antes de ejecutarlos |
| Exponer el puerto de la base de datos sin necesidad | Se confunde comunicación interna con publicación | Publica solo la entrada web y prueba la red interna |
| No comprobar el artefacto generado | La build «termina» pero falta un recurso | Lista el contenido y ejecuta una prueba funcional controlada |
9. Resumen y conclusión¶
En este tema has aprendido que:
- una aplicación web se entiende siguiendo el recorrido desde la fuente hasta el runtime y los datos;
- los recursos estáticos, dinámicos, de configuración, dependencias, persistencia y distribución tienen funciones y ciclos de vida diferentes;
pom.xml,package.json,composer.json,Dockerfileycompose.yamlaportan información distinta y no son intercambiables;- la estructura del proyecto no es universal: el runtime, el artefacto y la documentación deben confirmar lo que sugiere el nombre de una carpeta;
- un inventario útil relaciona archivos con procesos, puertos, servicios, redes, almacenamiento y pruebas;
- la base de datos y las cargas de usuarios no deben perderse al recrear una imagen o un contenedor.
Idea clave
Analizar la estructura de una aplicación es convertir un conjunto de archivos en un mapa de ejecución comprobable. Cuando sabes qué se construye, qué se ejecuta, qué se configura y qué debe persistir, puedes elegir el despliegue y diagnosticar sus fallos con fundamento.
10. Para seguir practicando¶
- Inspecciona la estructura de la aplicación PHP con MariaDB y relaciona
index.php,schema.sql,script.sh, la imagen y el volumen. - Abre la práctica de Java y Tomcat y dibuja la relación entre el WAR,
WEB-INF,webappsy el runtime de Tomcat. - Analiza WordPress con MariaDB y separa recursos de aplicación, configuración, red y persistencia.
- Completa una ficha de inventario para un proyecto que contenga
package.json,dist/, unDockerfiley una base de datos externa. - Comprueba el contenido de un WAR o una imagen sin modificar el entorno y anota qué evidencia responde a cada campo de la ficha.
Bibliografía y fuentes¶
- Normativa del módulo 0614.
- Material propio: servidores de aplicaciones.
- Material propio: creación de imágenes Docker.
- Material propio: web dinámica.
- Documentación oficial de Docker.
- Guía de Maven: estructura del proyecto.
- Documentación de npm.