Convenciones
4 artículos
Estándar de Codificación y Gestión de Errores en Arquitecturas de Microservicios
- Mauricio ECR
- Convenciones
- 18 Jun, 2025
En el ecosistema de aplicaciones web modernas, la arquitectura de microservicios distribuidos se ha consolidado como un paradigma dominante. Su flexibilidad, escalabilidad y resiliencia son innegables
Estándar de Codificación y Gestión de Errores en Arquitecturas de Microservicios
- Mauricio ECR
- Convenciones
- 18 Jun, 2025
En el ecosistema de aplicaciones web modernas, la arquitectura de microservicios distribuidos se ha consolidado como un paradigma dominante. Su flexibilidad, escalabilidad y resiliencia son innegables. Sin embargo, esta distribución introduce una complejidad significativa en la gestión de estados y, especialmente, en el manejo de errores. Cuando una operación involucra a múltiples servicios, identificar la causa raíz de un fallo puede convertirse en una tarea titánica, afectando la experiencia del usuario, los tiempos de resolución y la mantenibilidad del sistema.
Este documento establece un estándar completo y directamente aplicable para la codificación, documentación y gestión de errores visibles al usuario final. El objetivo es crear un marco de trabajo robusto que, aunque inicialmente se implementará en un entorno de backend con Java (Spring Boot) y frontend con Angular, ha sido diseñado con un enfoque agnóstico al lenguaje para garantizar su longevidad y adaptabilidad a futuras tecnologías.
Adoptar este estándar permitirá no solo presentar mensajes de error claros y útiles al usuario, sino también establecer una base documental sólida que facilite la observabilidad, la trazabilidad y la colaboración entre equipos de desarrollo, QA y soporte técnico.
El Estándar Detallado
1. Filosofía y Principios Fundamentales
Antes de detallar la implementación, es crucial entender los principios que guían este estándar:
- El Error como Ciudadano de Primera Clase: Los errores no son un caso excepcional, sino una parte inherente del flujo de una aplicación. Deben ser diseñados, documentados y probados con el mismo rigor que las funcionalidades exitosas.
- Claridad para el Usuario, Detalle para el Desarrollador: La información presentada al usuario final debe ser concisa, clara, traducible y orientada a la acción. Por el contrario, la información registrada para los equipos técnicos debe ser rica en detalles para permitir un diagnóstico rápido y preciso.
- Seguridad por Opacidad: Los códigos y mensajes de error públicos nunca deben revelar detalles de la infraestructura interna, nombres de clases, trazas de pila (stack traces) o cualquier información que pueda ser explotada por un actor malicioso.
- Trazabilidad Extrema: Cada error debe ser unívocamente identificable y correlacionable a través de los distintos servicios y sistemas de observabilidad (logs, métricas, trazas distribuidas).
2. Formato del Código de Error
Todo error visible al usuario final o que cruce los límites de un microservicio deberá ser identificado por un código único y opaco. Este código actúa como una clave inmutable que desacopla el error en sí de su representación (el mensaje).
La estructura propuesta es: MSS-ECNNN[-EXTCODE]
MSS(MicroService Short-identifier): Identificador numérico único de 3 dígitos asignado a cada microservicio. Esta asignación debe ser gestionada en un registro centralizado para evitar colisiones.- Ejemplo:
101para "Servicio de Autenticación",205para "Servicio de Pedidos".
- Ejemplo:
EC(Error Category): Código alfabético de 2 letras que clasifica la naturaleza del error. Esto permite un filtrado y análisis rápido. (Ver sección 3 para el catálogo de categorías).- Ejemplo:
IVpara "Validación de Entrada",BRpara "Regla de Negocio".
- Ejemplo:
NNN(Numeric Sequence): Secuencia numérica de 3 dígitos, única dentro del microservicio para esa categoría. Se recomienda iniciar en001y aumentar de forma secuencial.- Ejemplo:
001,002,047.
- Ejemplo:
[EXTCODE](External Code - Opcional): Componente opcional para encapsular errores originados en servicios de terceros. Proporciona una correlación directa sin exponer el formato original del tercero.- Formato:
EXT_<ID_SERVICIO>_<CODIGO_ERROR_ORIGINAL> ID_SERVICIO: Un identificador corto y predefinido para el servicio externo (ej.STRIPE,SENDGRID,AWS_S3).CODIGO_ERROR_ORIGINAL: El código de error devuelto por el servicio externo, sanitizado para ser compatible con la URL (ej. reemplazando espacios o caracteres especiales por_).- Ejemplo:
205-DP003-EXT_STRIPE_card_declined
- Formato:
Ejemplo completo: 205-BR001 representa el primer error de "Regla de Negocio" definido en el microservicio "Pedidos" (ID 205).
3. Clasificación de Errores Comunes
La estandarización de categorías (EC) es fundamental para la observabilidad y la generación de métricas. El catálogo inicial es el siguiente:
| Código | Categoría | Descripción |
|---|---|---|
IV |
Input Validation | Errores relacionados con la validación de datos de entrada (formato, rango, campos obligatorios). Suelen ser errores del tipo 400 Bad Request. |
AU |
Authentication & Authorization | Fallos de autenticación (token inválido, credenciales incorrectas) o autorización (sin permisos para la acción). Errores 401 Unauthorized o 403 Forbidden. |
BR |
Business Rule | Violación de una regla de negocio específica. La solicitud es sintácticamente correcta pero semánticamente inválida en el contexto actual (ej. stock insuficiente). Suele ser un 409 Conflict o 422 Unprocessable Entity. |
DP |
Dependency Failure | Un servicio externo del que depende el microservicio no está disponible o ha devuelto un error (otra API interna, base de datos, sistema de colas). Errores del tipo 502 Bad Gateway o 503 Service Unavailable. |
SY |
System Error | Errores inesperados o no controlados en el servidor (excepciones RuntimeException, NullPointerException, etc.). Siempre deben ser investigados. Corresponden a un 500 Internal Server Error. |
NT |
Network & Timeout | Errores de comunicación, ya sea porque una dependencia no respondió a tiempo o por problemas en la red. Corresponde a un 504 Gateway Timeout. |
CF |
Configuration Error | El servicio no puede iniciarse o funcionar correctamente debido a una configuración faltante o incorrecta (variables de entorno, secretos, etc.). |
NF |
Not Found | El recurso solicitado no existe. Corresponde a un 440 Not Found. |
4. Catálogo Inicial de Códigos de Error
A continuación, se presenta un catálogo de ejemplo con 10 códigos para dos microservicios hipotéticos: Gestión de Usuarios (ID: 101) y Procesamiento de Pedidos (ID: 205).
| Código | Mensaje usuario | Descripción técnica | Severidad | Estado | Acción esperada | Servicio externo (si aplica) |
|---|---|---|---|---|---|---|
| 101-IV001 | El correo electrónico proporcionado no tiene un formato válido. Por favor, revísalo. | El campo email en el DTO de registro de usuario no supera la validación de la expresión regular ^(.+)@(.+)$. |
Baja | Activo | Usuario: Corregir el formato del email. Frontend: Realizar validación en cliente para prevenir el error. | N/A |
| 101-AU001 | Tu sesión ha expirado. Por favor, inicia sesión de nuevo. | El JWT recibido en la cabecera Authorization ha caducado. La fecha de expiración (exp) es anterior a la fecha actual. |
Media | Activo | Sistema: Redirigir al usuario a la página de login. Usuario: Volver a introducir sus credenciales. | N/A |
| 101-AU002 | No tienes permisos para realizar esta acción. Contacta al administrador si crees que es un error. | El usuario autenticado, extraído del token, no posee el rol requerido (ROLE_ADMIN) para acceder al endpoint. |
Media | Activo | Frontend: Ocultar o deshabilitar la opción que provoca el error. Soporte: Verificar los roles del usuario si este levanta un ticket. | N/A |
| 101-BR001 | El correo electrónico ya está registrado en nuestra plataforma. Intenta iniciar sesión. | Se intentó crear un usuario con un email que ya existe en la tabla users de la base de datos (violación de UNIQUE constraint). |
Baja | Activo | Usuario: Usar la opción "recuperar contraseña" o iniciar sesión. Frontend: Ofrecer un enlace directo a la página de login. | N/A |
| 205-IV001 | El identificador del producto no es válido. Debe ser un número positivo. | El productId recibido en la línea de un pedido es nulo, cero o negativo. La validación @Positive falló en el DTO. |
Baja | Activo | Usuario: Informar del error. Es un caso raro que indica un bug en el frontend. Desarrollo: Investigar cómo se pudo enviar un ID inválido desde el cliente. | N/A |
| 205-BR001 | No hay suficiente stock para el producto 'Nombre del Producto'. Solo quedan X unidades. | La cantidad solicitada de un producto es mayor que el stock disponible registrado en la base de datos para ese productId. |
Media | Activo | Usuario: Reducir la cantidad del producto o eliminarlo del carrito. Sistema: El mensaje debe incluir el stock actual para ser útil. | N/A |
| 205-BR002 | No se pueden añadir productos de diferentes vendedores en un mismo pedido. | La lógica de negocio impide mezclar productos de sellerId distintos en una sola transacción para simplificar la logística. |
Media | Activo | Usuario: Finalizar la compra actual y crear un nuevo pedido para los otros productos. Frontend: Mostrar una advertencia al intentar añadir un producto incompatible. | N/A |
| 205-DP001 | El servicio de inventario no está respondiendo. Por favor, inténtalo de nuevo en unos minutos. | La llamada HTTP al microservicio de Inventario (ID 310) para verificar el stock ha resultado en un timeout o error 5xx. |
Alta | Activo | Sistema: Implementar un reintento con exponential backoff. Si persiste, activar un circuit breaker. Soporte: Revisar el estado del servicio de Inventario. | N/A |
| 205-DP002 | Tu pago no pudo ser procesado por el proveedor. Razón: Fondos insuficientes. | La pasarela de pagos (Stripe) devolvió un error 402 Request Failed con el código card_declined y el motivo insufficient_funds. |
Media | Activo | Usuario: Intentar con otro método de pago o verificar los fondos de su tarjeta. Sistema: No reintentar automáticamente. Invalidar el pedido. | EXT_STRIPE_insufficient_funds |
| 205-SY001 | Ha ocurrido un error inesperado al procesar tu pedido. Nuestro equipo ya ha sido notificado. | Se ha capturado una NullPointerException no esperada en la clase OrderProcessingService al calcular los impuestos. Se ha creado una alerta en Sentry/Datadog. |
Alta | Activo | Sistema: Registrar el trace_id y toda la información de la petición. Desarrollo: Priorizar la corrección de este bug. Usuario: Reintentar más tarde o contactar a soporte con el trace_id si se le muestra. |
N/A |
5. Documentación y Centralización
La documentación es lo que transforma este estándar de una idea a una herramienta útil.
- Estructura de Archivos: Cada microservicio debe incluir en su repositorio un archivo
docs/errors/errors.md. Este archivo contendrá la tabla completa de errores que el servicio puede generar. - Control de Versiones: El archivo
errors.mddebe ser versionado junto con el código fuente del microservicio. Cualquier adición o modificación de un código de error debe formar parte de un Pull Request, permitiendo su revisión. - Formato de Documentación: Se utilizará la tabla Markdown definida en la sección anterior. Este formato es fácil de leer para humanos y de parsear por máquinas.
Ejemplo de microservice-orders/docs/errors/errors.md:
Catálogo de Errores - Servicio de Pedidos
- Nombre Lógico: Servicio de Procesamiento de Pedidos
- ID del Microservicio: 205
| Código | Mensaje usuario | Descripción técnica | Severidad | Estado | Acción esperada | Servicio externo (si aplica) |
|---|---|---|---|---|---|---|
| 205-IV001 | El identificador del producto no es válido. Debe ser un número positivo. | El productId recibido en la línea de un pedido es nulo, cero o negativo. La validación @Positive falló en el DTO. |
Baja | Activo | Usuario: Informar del error. Desarrollo: Investigar cómo se pudo enviar un ID inválido. | N/A |
| ... | ... | ... | ... | ... | ... | ... |
6. Recomendaciones para Centralización y Escalabilidad
Para que el estándar sea efectivo en una organización grande, se recomienda:
- Repositorio Central de Errores: Un pipeline de CI/CD debería agregar los archivos
errors.mdde todos los microservicios tras cada release exitosa en un repositorio central o una página de Confluence/Wiki. Esto crea un único punto de verdad para que los equipos de QA, Soporte y Frontend puedan consultar cualquier código de error sin necesidad de acceder a los repositorios individuales. - Automatización y Linters: El pipeline de CI/CD debe incluir pasos para:
- Validar la Unicidad: Asegurar que no existan códigos
MSS-ECNNNduplicados dentro del mismo microservicio. - Validar el Formato: Comprobar que todos los nuevos códigos sigan la estructura definida.
- Detectar Nuevos Servicios Externos: Lanzar una advertencia si se añade un
EXTCODEcon un<ID_SERVICIO>no registrado previamente, para asegurar que se documente centralmente.
- Validar la Unicidad: Asegurar que no existan códigos
- Generación de Artefactos: Se pueden crear scripts que lean estos archivos
.mdpara generar automáticamente artefactos útiles, como:- Enums de TypeScript para el frontend, permitiendo un manejo de errores tipado (
case ErrorCodes.USER_EMAIL_EXISTS:). - Clases de constantes en Java para el backend.
- Plantillas para sistemas de tickets (Jira, Zendesk).
- Enums de TypeScript para el frontend, permitiendo un manejo de errores tipado (
Conclusión
Este estándar de codificación y gestión de errores proporciona un marco de trabajo unificado y resiliente, diseñado para escalar con la complejidad de una arquitectura de microservicios. Al tratar los errores como un componente central del diseño de software, logramos múltiples beneficios:
- Mejora la Experiencia del Usuario (UX): Los mensajes son claros, consistentes y orientados a la acción.
- Agiliza la Resolución de Incidencias: Los códigos únicos y la documentación detallada permiten a los equipos de soporte y desarrollo identificar y solucionar problemas rápidamente.
- Potencia la Observabilidad: La estructura de códigos y categorías facilita la creación de dashboards, alertas y métricas significativas sobre la salud del sistema.
- Aumenta la Seguridad: La opacidad de los códigos previene la fuga de información sensible de la infraestructura.
Futuras Líneas de Aplicación:
- Integración con Plataformas de Observabilidad: Enriquecer automáticamente las trazas distribuidas (ej. en Jaeger o Datadog) con la "Descripción Técnica" del error a partir de su código.
- Desarrollo de un "Servicio de Errores": Una pequeña API central que, dado un código de error, devuelva su documentación completa, incluyendo el mensaje de usuario localizado en diferentes idiomas.
- Generación de SDKs de Cliente: Automatizar la creación de librerías para diferentes lenguajes (JS/TS, Python, Go) que encapsulen la lógica de manejo de estos códigos de error estandarizados.
La adopción disciplinada de este estándar no es una carga adicional, sino una inversión estratégica en la calidad, mantenibilidad y escalabilidad a largo plazo de la plataforma.
Descubre el Poder del SemVer: Optimiza el Versionado de tu Software y Mantén un CHANGELOG Excepcional
- Mauricio ECR
- Convenciones
- 08 Apr, 2025
El Versionado Semántico (SemVer) es una herramienta fundamental para comunicar de forma precisa los cambios en el software, facilitando el mantenimiento y la colaboración. Complementarlo con un **
Descubre el Poder del SemVer: Optimiza el Versionado de tu Software y Mantén un CHANGELOG Excepcional
- Mauricio ECR
- Convenciones
- 08 Apr, 2025
El Versionado Semántico (SemVer) es una herramienta fundamental para comunicar de forma precisa los cambios en el software, facilitando el mantenimiento y la colaboración. Complementarlo con un CHANGELOG bien estructurado potencia la transparencia y la trazabilidad, ofreciendo una visión detallada de la evolución de cada versión. En este artículo, exploraremos en profundidad cómo adoptar SemVer y cómo mantener un CHANGELOG que vaya de la mano para optimizar tu proceso de desarrollo.
Introducción
El control de versiones en el desarrollo de software se convierte en una ventaja competitiva cuando se implementa de forma clara y estructurada. SemVer aporta un sistema numérico que indica el alcance de los cambios (cambios mayores, menores o correcciones), mientras que el CHANGELOG documenta y narra el proceso evolutivo de tu proyecto. Esta sinergia no solo facilita la colaboración interna, sino que también mejora la comunicación con los usuarios y clientes, ayudando a identificar qué, cuándo y por qué se han realizado determinados cambios.
1. Estructura Básica de SemVer
El formato principal de SemVer se compone de tres segmentos:
MAJOR.MINOR.PATCH
- MAJOR: Se incrementa cuando se realizan cambios incompatibles con versiones anteriores (por ejemplo, la eliminación o modificación de una API pública).
- MINOR: Se aumenta cuando se agregan nuevas funcionalidades de manera compatible.
- PATCH: Se incrementa al corregir errores sin afectar las funcionalidades existentes.
Etiquetas Adicionales
- Pre-release: Indica versiones inestables, por ejemplo,
1.0.0-beta.1. - Build Metadata: Añade información adicional de compilación, como en
1.0.0+20230901.
2. Reglas para Incrementar Versiones
Al actualizar una versión, es fundamental distinguir entre los diferentes tipos de cambios:
Versión MAJOR (X.y.z → X+1.0.0):
Se utiliza cuando se introducen cambios que rompen la compatibilidad con versiones anteriores.
Ejemplo: Modificar o eliminar una API de forma incompatible.Versión MINOR (x.Y.z → x.Y+1.0):
Se incrementa al introducir nuevas funcionalidades sin afectar la compatibilidad.
Ejemplo: Agregar un método opcional a una clase.Versión PATCH (x.y.Z → x.y.Z+1):
Se utiliza para corregir errores, preservando la compatibilidad con versiones anteriores.
Ejemplo: Arreglar un error en una función de cálculo.
3. Ejemplos Prácticos
Versión Inicial:
0.1.0
Indica la fase de desarrollo inicial donde el software puede sufrir cambios drásticos.Primera Versión Estable:
1.0.0
Marca el lanzamiento oficial cuando la API es considerada estable y está documentada.Actualización con Nueva Funcionalidad:
De1.0.0a1.1.0para incorporar mejoras sin romper compatibilidad.Corrección Crítica:
De1.1.0a1.1.1para solucionar errores puntuales.Cambio Incompatible:
De1.1.1a2.0.0cuando se realizan modificaciones que requieren cambios en el código del consumidor.
4. La Importancia de Mantener un CHANGELOG
Un CHANGELOG es un registro sistemático y estructurado que documenta de manera cronológica cada cambio, mejora y corrección en el software. Su incorporación al proceso de SemVer proporciona un contexto narrativo, detallando el "porqué" y el "cómo" detrás de cada versión.
Beneficios Clave del CHANGELOG
Claridad en la Comunicación:
Complementa los números de versión de SemVer con descripciones detalladas de los cambios implementados.Trazabilidad y Historial:
Permite rastrear la evolución del software a lo largo del tiempo, facilitando la depuración y la revisión histórica.Transparencia Interna y Externa:
Informa tanto a los desarrolladores como a los usuarios finales sobre las mejoras y cambios realizados, fortaleciendo la confianza en el proceso de actualización.Soporte a la Automatización:
Herramientas integradas pueden actualizar el CHANGELOG de forma automática al seguir convenciones de commits, como Conventional Commits.
Mejores Prácticas para un CHANGELOG Efectivo
Estructuración Clara:
Organiza el CHANGELOG por versiones, empezando por la más reciente. Dentro de cada versión, clasifica los cambios en secciones como "Añadido", "Modificado", "Corregido" y "Notas de Deprecación".Actualización Continua:
Registra los cambios a medida que se van implementando para capturar detalles precisos y evitar omisiones.Integración con el Proceso de Versionado:
Vincula cada entrada del CHANGELOG con commits específicos y números de versión, facilitando la sincronización entre lo que se comunica y lo que se versiona.Comunicación Externa:
Publica el CHANGELOG en el repositorio del proyecto y en las notas de lanzamiento para que usuarios y colaboradores comprendan la evolución del software.
Ejemplo Básico de CHANGELOG
# CHANGELOG
## [2.0.0] - 2025-04-01
### Añadido
- Nueva función para exportación de datos.
### Modificado
- Actualización del sistema de autenticación (rompe compatibilidad con versiones anteriores).
### Corregido
- Error en el módulo de notificaciones.
## [1.2.1] - 2025-03-15
### Corregido
- Solucionado el error en la actualización automática del perfil del usuario.
5. Herramientas para Generar CHANGELOG Automáticamente
Usar herramientas automatizadas facilita enormemente el mantenimiento de un CHANGELOG claro y actualizado. A continuación, se presentan algunas opciones destacadas:
Conventional Changelog
- Base de muchas herramientas automatizadas.
- Genera el
CHANGELOG.mda partir de commits con formato estándar. - Ejemplo:
npx conventional-changelog -p angular -i CHANGELOG.md -s
standard-version
- Automatiza el versionado y el changelog sin publicar a npm.
- Ideal para control manual con automatización parcial.
npm install --save-dev standard-version
npx standard-version
semantic-release
- Automatiza TODO: changelog, versionado, publicación en npm o GitHub.
- Requiere entorno CI/CD (ej: GitHub Actions).
npm install --save-dev semantic-release
release-it
- Personalizable, ideal para flujos mixtos.
npm install --save-dev release-it
npx release-it
6. Convenciones de Commit (Conventional Commits)
Estas convenciones estructuran los mensajes de commit para que puedan ser procesados automáticamente:
<tipo>(opcional: alcance): descripción
[opcional] cuerpo del mensaje
[opcional] notas de ruptura (BREAKING CHANGE)
Ejemplos:
feat(auth): agregar login con Google
fix(api): corregir error de serialización
docs(readme): actualizar instrucciones de uso
BREAKING CHANGE: se eliminó el endpoint /v1/user
Tipos más comunes:
| Tipo | Uso |
|---|---|
feat |
Nueva funcionalidad |
fix |
Corrección de errores |
docs |
Cambios en la documentación |
style |
Cambios de formato (espacios, comas, etc.) |
refactor |
Refactorización del código sin cambios de funcionalidad |
test |
Cambios relacionados a pruebas |
chore |
Tareas menores de mantenimiento |
7. Gestión de Dependencias
El versionado semántico también afecta cómo se definen y gestionan las dependencias en los proyectos:
Caret ( ^ ):
Permite actualizaciones de versiones MINOR y PATCH. Por ejemplo,^1.2.3abarca versiones de la serie1.x.x.Tilde ( ~ ):
Restringe las actualizaciones a parches solamente. Por ejemplo,~1.2.3asegura que se mantenga la versión1.2.x.
8. Herramientas Recomendadas
- semver: Librería para comparar y validar versiones.
- Conventional Commits: Estándar de mensajes para facilitar changelogs automáticos.
- GitHub Actions/GitLab CI: Automatiza versiones y publicaciones.
- semantic-release / standard-version / release-it: Para generar changelogs y manejar versiones automáticamente.
Tabla Resumen
| Aspecto | Descripción | Ejemplo |
|---|---|---|
| Formato Básico de SemVer | MAJOR.MINOR.PATCH | 2.4.1 |
| Versión MAJOR | Cambios incompatibles que rompen versiones anteriores | 1.1.1 → 2.0.0 |
| Versión MINOR | Nuevas funcionalidades sin romper compatibilidad | 1.0.0 → 1.1.0 |
| Versión PATCH | Correcciones de errores sin afectar la funcionalidad | 1.1.0 → 1.1.1 |
| Pre-release | Versión inestable para pruebas | 1.0.0-beta.1 |
| Build Metadata | Información adicional de compilación | 1.0.0+20230901 |
| Gestión de Dependencias | Uso de caret (^) para permitir MINOR y PATCH; tilde (~) para solo PATCH | ^1.2.3 y ~1.2.3 |
| CHANGELOG | Registro detallado de cambios, organizado por versiones y secciones claras | Ver ejemplo en el artículo |
| Convenciones de Commit | Estilo estructurado que facilita la generación automática de changelog | feat, fix, chore, etc. |
| Herramientas de Automatización | Facilitan la gestión de versiones y changelog | semantic-release, standard-version, release-it |
Conclusión
Integrar el Versionado Semántico con un CHANGELOG completo y bien documentado garantiza un proceso de actualización y mantenimiento de software más transparente y predecible. Adoptar ambas prácticas no solo mejora la organización interna y el flujo de trabajo, sino que también fortalece la comunicación con los usuarios al explicar de forma detallada cada cambio realizado. Utiliza esta guía para transformar tu estrategia de versionado y construir una base sólida para el crecimiento y la evolución de tu proyecto de software.
Estructuración de Carpetas en Proyectos de Software
- Mauricio ECR
- Convenciones
- 18 Mar, 2025
En proyectos de software de gran escala, la falta de una estructura de carpetas bien definida puede generar desorden, dificultando la mantenibilidad, escalabilidad y comprensión del código. Muchas vec
Estructuración de Carpetas en Proyectos de Software
- Mauricio ECR
- Convenciones
- 18 Mar, 2025
En proyectos de software de gran escala, la falta de una estructura de carpetas bien definida puede generar desorden, dificultando la mantenibilidad, escalabilidad y comprensión del código. Muchas veces, los proyectos crecen de manera descontrolada sin una estructura definida, lo que lleva a código difícil de navegar, dependencias circulares entre módulos, acoplamiento innecesario entre componentes y dificultad para incorporar nuevos desarrolladores al equipo.
Para estructurar un proyecto de software de manera eficiente, es recomendable seguir una organización de carpetas que refleje claramente la separación de responsabilidades. Esto ayuda a mantener un código modular, organizado y fácil de mantener.
Los principios clave para la organización de carpetas incluyen modularidad, donde cada módulo representa una unidad de negocio independiente; independencia, separando la lógica de negocio de la infraestructura y los adaptadores; cohesión, manteniendo agrupados los elementos relacionados dentro de un módulo; y evolución, permitiendo la escalabilidad sin afectar la estructura global.
Una estructura de carpetas recomendada puede verse de la siguiente manera:
/project-root
├── src
│ ├── core # Reglas de negocio y modelos
│ │ ├── domain
│ │ │ ├── entities
│ │ │ ├── value_objects
│ │ │ ├── repositories
│ │ │ ├── services
│ │ │ └── events
│ │ ├── application
│ │ │ ├── use_cases
│ │ │ ├── dtos
│ │ │ ├── mappers
│ │ │ └── queries
│ ├── modules # Módulos específicos del negocio
│ │ ├── user_management
│ │ │ ├── domain
│ │ │ ├── application
│ │ │ ├── infrastructure
│ │ │ ├── adapters
│ ├── infrastructure # Implementaciones técnicas y frameworks
│ │ ├── persistence
│ │ ├── messaging
│ │ ├── external_services
│ │ ├── config
│ ├── adapters # Interfaces externas y controladores
│ │ ├── api
│ │ ├── cli
│ │ ├── event_consumers
│ │ └── schedulers
├── tests # Pruebas unitarias y de integración
├── docs # Documentación del proyecto
├── scripts # Herramientas para automatización
├── config # Configuración general del proyecto
├── .gitignore
├── README.md
Dentro de esta estructura, 'core/' contiene la lógica de negocio (entidades, servicios, repositorios, etc.); 'modules/' agrupa los módulos del dominio de manera aislada; 'infrastructure/' contiene implementaciones técnicas como bases de datos, servicios externos y configuraciones; 'adapters/' define las interfaces de comunicación con el mundo exterior, incluyendo APIs, CLI y eventos; 'tests/' contiene pruebas unitarias e integración; 'docs/' almacena documentación técnica del proyecto; 'config/' centraliza archivos de configuración; y 'scripts/' incluye herramientas de automatización.
Por ejemplo, en un sistema de gestión de usuarios con autenticación, podríamos estructurar el módulo correspondiente de la siguiente manera:
/modules/user_management
├── domain
│ ├── entities
│ │ ├── UserEnt.ts
│ ├── value_objects
│ │ ├── EmailVO.ts
│ ├── repositories
│ │ ├── UserRepo.ts
├── application
│ ├── use_cases
│ │ ├── RegisterUserUC.ts
│ │ ├── AuthenticateUserUC.ts
│ ├── dtos
│ │ ├── UserDTO.ts
│ ├── mappers
│ │ ├── UserMap.ts
├── infrastructure
│ ├── persistence
│ │ ├── UserRepoImpl.ts
├── adapters
│ ├── api
│ │ ├── UserCtrl.ts
Aquí, la capa de dominio maneja las reglas de negocio, la capa de aplicación contiene los casos de uso, la infraestructura gestiona la persistencia y los adaptadores exponen las funcionalidades a través de controladores API.
Resumen de Carpetas y su Uso
| Carpeta | Descripción | Ejemplo en 'user_management' |
|---|---|---|
| 'core/domain' | Contiene entidades, objetos de valor, repositorios y servicios | 'UserEnt.ts' define la entidad de usuario |
| 'core/application' | Define casos de uso, DTOs, mappers y queries | 'RegisterUserUC.ts' implementa la lógica de registro de usuario |
| 'modules' | Agrupa módulos específicos del negocio | 'user_management/' encapsula toda la gestión de usuarios |
| 'domain/entities' | Modelos de negocio que representan objetos persistentes | 'UserEnt.ts' representa los datos del usuario en la base de datos |
| 'domain/value_objects' | Objetos de valor inmutables del dominio | 'EmailVO.ts' maneja la lógica del correo electrónico |
| 'domain/repositories' | Interfaces para acceso a datos | 'UserRepo.ts' define la interfaz para obtener usuarios |
| 'application/use_cases' | Casos de uso que orquestan la lógica de negocio | 'RegisterUserUC.ts' contiene la lógica de registro de usuario |
| 'application/dtos' | Transporte de datos entre capas | 'UserDTO.ts' define el formato de los datos de usuario |
| 'application/mappers' | Conversión entre entidades y DTOs | 'UserMap.ts' convierte entre 'UserEnt' y 'UserDTO' |
| 'infrastructure/persistence' | Implementación de acceso a datos | 'UserRepoImpl.ts' implementa 'UserRepo.ts' |
| 'adapters/api' | Exposición de funcionalidades vía API | 'UserCtrl.ts' maneja solicitudes HTTP |
| 'tests' | Contiene pruebas unitarias y de integración | 'UserSvcTest.ts' prueba 'RegisterUserUC.ts' |
| 'docs' | Documentación técnica del proyecto | Documentación sobre el flujo de autenticación |
| 'config' | Configuración general de la aplicación | Configuración de base de datos y variables de entorno |
| 'scripts' | Scripts de automatización | Scripts para migraciones o configuración |
Implementar una estructura de carpetas bien organizada ayuda a crear proyectos más mantenibles, escalables y fáciles de entender. Al separar claramente las responsabilidades, evitamos el acoplamiento innecesario y mejoramos la colaboración dentro del equipo de desarrollo. Desde el inicio del proyecto, es recomendable definir y documentar la estructura de carpetas para que todo el equipo la adopte y mantenga. Con esta estructura clara y modular, los proyectos de software pueden escalar sin comprometer la calidad del código. 🚀
Convención de Nombres en Clases Java: Mejora de Legibilidad y Mantenibilidad
- Mauricio ECR
- Convenciones
- 18 Mar, 2024
En proyectos de gran escala, identificar rápidamente el tipo de una clase facilita la comprensión del código, la colaboración y la depuración. Sin un esquema de nombres estandarizado, es común que los
Convención de Nombres en Clases Java: Mejora de Legibilidad y Mantenibilidad
- Mauricio ECR
- Convenciones
- 18 Mar, 2024
En proyectos de gran escala, identificar rápidamente el tipo de una clase facilita la comprensión del código, la colaboración y la depuración. Sin un esquema de nombres estandarizado, es común que los desarrolladores enfrenten dificultades al interpretar la funcionalidad de una clase solo por su nombre, lo que ralentiza el mantenimiento y el desarrollo. En entornos profesionales, donde múltiples equipos trabajan sobre el mismo código base, contar con una nomenclatura clara mejora la comunicación y reduce la posibilidad de errores. Para solucionar esto, se propone una convención de nombres basada en prefijos y sufijos que permita diferenciar los distintos tipos de clases en Java.
Para garantizar la claridad, adoptamos las siguientes reglas:
- Prefijos:
- Enumeraciones: E (Ejemplo: EUserRole)
- Interfaces: I (Ejemplo: IUserService)
- Sufijos:
- Entities (Ent): Representan objetos persistentes. Ejemplo: UserEnt
- Value Objects (VO): Objetos inmutables con valor significativo. Ejemplo: MoneyVO
- Data Transfer Objects (DTO): Transporte de datos entre capas. Ejemplo: UserDTO
- Repositories (Repo): Acceso a datos. Ejemplo: UserRepo
- Services (Svc): Lógica de negocio. Ejemplo: UserSvc
- Controllers (Ctrl): Gestionan solicitudes HTTP. Ejemplo: UserCtrl
- Exceptions (Ex): Manejo de errores. Ejemplo: InvalidUserEx
- Configuration (Cfg): Configuraciones de aplicación. Ejemplo: DatabaseCfg
- Utility (Util): Métodos reutilizables. Ejemplo: DateUtil
- Test (Test): Clases de prueba. Ejemplo: UserSvcTest
- Commands (Cmd): Acciones en el sistema. Ejemplo: CreateUserCmd
- Domain Events (Evt): Eventos de dominio. Ejemplo: UserCreatedEvt
- Validation (Val): Validaciones. Ejemplo: UserVal
- Adapters (Adp): Integraciones con otros sistemas. Ejemplo: ExternalServiceAdp
- Message Brokers (Brk): Comunicación con colas de mensajes. Ejemplo: KafkaMsgBrk
- Mappers (Map): Transformación de objetos. Ejemplo: UserMap
- Views (View): Modelos de presentación. Ejemplo: UserView
- Gateways (Gtw): Comunicación con servicios externos. Ejemplo: PaymentGtw
- Factories (Fct): Creación de objetos. Ejemplo: UserFct
- Specifications (Spec): Criterios de búsqueda. Ejemplo: UserSpec
- Middleware (Mid): Interceptores de solicitudes. Ejemplo: AuthMid
- Aggregates (Agg): Raíces de agregados DDD. Ejemplo: OrderAgg
- Use Cases (UC): Casos de uso específicos. Ejemplo: RegisterUserUC
- Queries (Qry): Consultas a la base de datos. Ejemplo: FindUserQry
- View Models (VM): Modelos de datos para UI. Ejemplo: UserVM
- Policy / Guard (Pol o Grd): Reglas de negocio. Ejemplo: UserAccessPol
Para aplicar esta convención en un proyecto, recomendamos documentarla dentro del repositorio, integrar validaciones automáticas en las revisiones de código mediante linters o revisiones de PR y aplicar la convención en nuevos desarrollos, refactorizando el código legado progresivamente.
Adoptar una convención de nombres en clases Java mejora la comprensión del código y la colaboración en equipos de desarrollo. Al estandarizar prefijos y sufijos, cada clase tiene un propósito claro y es más fácil de ubicar y utilizar. Implementar esta convención desde el inicio de un proyecto o refactorizar el código existente progresivamente traerá beneficios a largo plazo.
| Tipo de Clase | Prefijo/Sufijo | Ejemplo | Descripción |
|---|---|---|---|
| Enumeraciones | E | EUserRole | Define un conjunto de valores constantes |
| Interfaces | I | IUserService | Define contratos que deben implementar las clases |
| Entities | Ent | UserEnt | Representa objetos persistentes en la base de datos |
| Value Objects | VO | MoneyVO | Objetos inmutables que representan valores |
| DTOs | DTO | UserDTO | Transporte de datos entre capas |
| Repositories | Repo | UserRepo | Acceso a la base de datos |
| Services | Svc | UserSvc | Contiene la lógica de negocio |
| Controllers | Ctrl | UserCtrl | Gestiona las solicitudes HTTP |
| Exceptions | Ex | InvalidUserEx | Manejo de errores y excepciones |
| Configuration | Cfg | DatabaseCfg | Configuraciones de la aplicación |
| Utilities | Util | DateUtil | Métodos reutilizables |
| Tests | Test | UserSvcTest | Pruebas unitarias y de integración |
| Commands | Cmd | CreateUserCmd | Representa una acción o comando |
| Domain Events | Evt | UserCreatedEvt | Eventos del dominio |
| Validation | Val | UserVal | Validaciones de datos |
| Adapters | Adp | ExternalServiceAdp | Integración con sistemas externos |
| Message Brokers | Brk | KafkaMsgBrk | Comunicación con colas de mensajes |
| Mappers | Map | UserMap | Conversión entre objetos |
| Views | View | UserView | Modelos de presentación |
| Gateways | Gtw | PaymentGtw | Comunicación con servicios externos |
| Factories | Fct | UserFct | Creación de instancias de objetos |
| Specifications | Spec | UserSpec | Definición de criterios de búsqueda |
| Middleware | Mid | AuthMid | Interceptores de solicitudes |
| Aggregates | Agg | OrderAgg | Raíz de un agregado en DDD |
| Use Cases | UC | RegisterUserUC | Casos de uso específicos |
| Queries | Qry | FindUserQry | Consultas a la base de datos |
| View Models | VM | UserVM | Modelos de datos para UI |
| Policy/Guard | Pol/Grd | UserAccessPol | Reglas de negocio y restricciones |