Caso de estudio · Sistema full‑stack en producción

Cielos Abiertos
Gestión de alabanza

Una PWA para que un ministerio de música organice su repertorio, arme las listas de cada culto, asigne al equipo y toque con letras, acordes y audio, desde el celular y con permisos por rol.

0permisos granulares
0tablas relacionadas
0módulos de negocio
0roles editables
0cambios documentados
Fase 1 · Relevamiento

El problema real

Cielos Abiertos nació de una necesidad concreta: el equipo de música de una iglesia coordinaba todo por WhatsApp, PDFs sueltos y memoria. El sistema reemplaza ese circuito sin cambiar la forma en que el equipo ya trabaja.

Situación de partida

El líder armaba la lista de cada domingo escribiéndola a mano en un chat. Letras y acordes vivían en archivos distintos, cada músico en un tono diferente y nadie sabía con certeza quién tocaba qué.

Dolores detectados

  • Listas perdidas entre mensajes
  • Acordes en tonos distintos según quién los pasara
  • Sin forma de saber si una canción se tocó hace poco
  • Cualquiera veía y editaba todo

Objetivo

Un único lugar, usable desde el celular durante el ensayo y el culto, donde la lista, el equipo, la letra, los acordes y el audio estén a un toque, con visibilidad según el rol de cada persona.

Usuarios

AdministradorLíderMúsicoInvitado

Usuarios no técnicos, mayormente en celular (incluido iPhone) y con conexión irregular dentro del templo.

✦Resultado en una frase

Un sistema de 17 tablas, 9 módulos y 36 permisos que el equipo usa cada semana, con un flujo de trabajo guiado por su propia forma de organizarse (tres alabanzas, dos adoraciones y la ofrenda).

Fase 2 · Análisis

Requisitos

Relevados con los propios líderes y refinados en iteraciones cortas. Cada uno es verificable.

IDRequisitoTipoPrioridad
RF-01Alta, edición y baja de canciones con tipo (alabanza/adoración), tono, BPM, etiquetas, links y letra con acordes.FuncionalAlta
RF-02Buscar y filtrar el repertorio por título, artista, tipo, etiqueta y tono.FuncionalAlta
RF-03Crear listas por fecha con secciones (alabanza, adoración, ofrenda), tono propio por canción y reordenamiento.FuncionalAlta
RF-04Asignar al equipo por instrumento, solo entre quienes pueden tocarlo.FuncionalAlta
RF-05Generar una lista aleatoria (3 alabanzas + 2 adoraciones + ofrenda) respetando temas y canciones enganchadas.FuncionalMedia
RF-06Ver letra y acordes, transponer de tono y exportar a PDF.FuncionalAlta
RF-07Reproductor con audio propio (pistas) o video de YouTube, con repetir tramo y cola.FuncionalMedia
RF-08Copiar la lista, el equipo y las notas con el formato que usan los líderes en WhatsApp.FuncionalAlta
RF-09Cada músico ve destacada la lista en la que está asignado.FuncionalMedia
RF-10Estadísticas de uso: canciones más tocadas, rotación y búsqueda en el historial.FuncionalBaja
RF-11Gestión de usuarios y roles; los roles y sus permisos se editan desde la propia aplicación.FuncionalAlta
RF-12Acceso de invitado de solo lectura, sin crear cuenta.FuncionalMedia
RNF-01Seguridad: contraseñas con hash, JWT, permisos verificados en el servidor en cada petición.No funcionalAlta
RNF-02Usabilidad móvil: sin desbordes entre 320 y 430 px, targets táctiles amplios, instalable como app.No funcionalAlta
RNF-03Rendimiento: carga inicial renderizada en servidor, listas paginadas y caché de consultas.No funcionalMedia
RNF-04Costo operativo mínimo: corre en planes gratuitos con mitigación del “arranque en frío”.No funcionalAlta
RNF-05Mantenibilidad: módulos por dominio, tipado estricto, tests y CI obligatorio antes de integrar.No funcionalAlta
RNF-06Trazabilidad: auditoría de creación y modificación y baja lógica de datos.No funcionalMedia
Fase 2 · Análisis

Alcance

Delimitar lo que NO se hace fue tan importante como lo que sí: mantiene el producto chico y mantenible.

✓Dentro del alcance

  • Repertorio, listas, equipo y roles de una congregación
  • Letra y acordes con transposición y PDF
  • Audio propio por canción y reproductor de YouTube
  • PWA instalable en Android e iOS
  • Permisos por rol editables en la interfaz
  • Estadísticas básicas e historial
  • Despliegue con CI y mitigación del arranque en frío

✕Fuera del alcance

  • Multi‑iglesia (multi‑tenant)
  • Notificaciones push y chat interno
  • Edición colaborativa en tiempo real
  • Modo offline completo de datos
  • Pagos o suscripciones
  • Apps nativas de tienda

Evolución natural: multi‑tenant y notificaciones, sin cambiar el modelo de permisos.

Fase 2 · Análisis

Casos de uso

Filtrá por actor y mirá qué puede hacer cada uno. Cada caso está atado a un permiso del sistema.

UC‑01 · Ver repertorio

Busca y filtra las canciones del ministerio.

cancion:ver

UC‑02 · Gestionar canciones

Crea, edita y da de baja canciones, con tags, links y pistas.

cancion:crear · editar · eliminar

UC‑03 · Ver letra y acordes

Abre la canción, transpone y fija el tamaño de letra.

cancion:ver

UC‑04 · Descargar PDF

Exporta la canción o la lista completa con acordes.

cancion:ver

UC‑05 · Reproducir audio

Escucha la pista o el video, repite un tramo y arma cola.

escuchar:ver

UC‑06 · Armar una lista

Elige fecha, secciones, canciones y tono de cada una.

lista:crear · editar

UC‑07 · Generar lista al azar

Sorteo ponderado 3+2+ofrenda respetando temas.

lista:crear

UC‑08 · Asignar equipo

Elige músicos por instrumento según lo que sepan tocar.

lista:editar

UC‑09 · Compartir por WhatsApp

Copia lista, equipo y notas con el formato del grupo.

lista:ver

UC‑10 · Ver mi lista asignada

Ve destacada la lista donde está en el equipo.

lista-propia:ver

UC‑11 · Usar listas predefinidas

Guarda y reutiliza plantillas de estructura.

plantilla:ver · crear

UC‑12 · Marcar favoritas

Guarda canciones para encontrarlas rápido.

favorito:crear

UC‑13 · Ver estadísticas

Más tocadas, rotación y búsqueda en el historial.

estadistica:ver

UC‑14 · Gestionar usuarios

Alta, baja lógica y asignación de roles e instrumentos.

usuario:*

UC‑15 · Editar roles y permisos

Define qué puede hacer cada rol, sin tocar código.

rol:editar

UC‑16 · Entrar como invitado

Accede de solo lectura sin crear cuenta.

sesión sin usuario
Fase 3 · Diseño

Arquitectura

Cliente y API desacoplados, desplegados por separado. Tocá cada bloque para ver su responsabilidad.

USUARIOS VERCEL · CLIENTE RENDER · API DATOS AUTOMATIZACIÓN Y SERVICIOS EXTERNOS Líder · MúsicoAdmin · Invitado PWA · React 19TanStack Router + Query SSR · TanStack StartNitro sobre Vercel Cliente HTTP + permisosJWT · caché · guards de ruta Guards JWT + RBACPassport · permisos en BD Módulos de dominioController · Service · DTO TypeORMEntidades · migraciones PostgreSQLNeon Cloudflare R2URLs firmadas GitHub Actions · CI + wake YouTube
Pasá el mouse o tocá un bloque para ver qué hace. Las líneas punteadas doradas muestran el camino de una petición: navegador → API → base de datos.
Fase 3 · Diseño

Diagrama de clases

Modelo de dominio. Los rombos marcan composición (el hijo no existe sin el padre); las flechas, asociaciones. Pasá el mouse sobre una clase para aislar sus relaciones.

Arrastrá para moverte · Ctrl+rueda para zoom
Acceso y roles Listas de canciones Canciones Interacción permisos1* roles** líder*1 equipo** items1* *1 *1 temas** pistas1* links1* reproducciones1* autor*1 *1 *1 *1 RolePermissionRolePermission+permission: string «recurso:acción» RoleRole+id: uuid+name: string User«audit»User+id: uuid+email: string+passwordHash: string+name: string+instruments: string[]+avatarKey: string? SetlistTemplate«audit»SetlistTemplate+title: string+items: json Setlist«audit»Setlist+id: uuid+title: string+date: Date+type: EventType+teamInstruments: json+notes: string? SetlistItemSetlistItem+key: string «tono de ese evento»+section: Alabanza|Adoración|Ofrenda?+position: int Song«audit»Song+id: uuid+title: string+artist: string+key: string+bpm: int+duration: int+chordpro: string+audioKey: string?+enganchado: bool?+proximaDesde: Date? TipoCancionTipoCancion+nombre: string «Alabanza | Adoración» TagTag+valor: string AudioTrackAudioTrack+label: string+audioKey: string+order: int SongLinkSongLink+url: string+order: int SongPlayStatSongPlayStat+month: string+plays: int FavoriteFavorite+userId: uuid+songId: uuid Annotation«audit»Annotation+text: string
Acceso y rolesListasCancionesInteracción◆ composición · → asociación · «audit» = hereda de la entidad base con auditoría
Fase 3 · Diseño

Diagramas de proceso

Dos flujos clave en carriles: quién hace cada paso. Podés animar el recorrido.

Fase 3 · Diseño

Estructura de código adoptada

Organización por funcionalidad (feature‑first) en ambos lados: lo que cambia junto vive junto.

frontend/src
features/ // una carpeta por módulo de negocio
musica/setlistscomponents/ · pages/ · lib/ // generator, sections, whatsapp
cancionescomponents/ · hooks/ · lib/ // medley, filtros
equipo · roles · estadisticas · inicioapi · hooks · pages
components/ // layout, common, ui (shadcn)
lib/ // chords.ts (parser/transponer), permisos, api
routes/ // rutas de archivo (TanStack Router)
backend/src
modules/ // un módulo Nest por dominio
setlistscontroller · service · dto/ · entities/ · public-team.ts
songs · roles · users · authcontroller · service · dto/ · entities/
storage · stats · favoritesURLs firmadas R2 · agregaciones
common/ // guards, decoradores, entidad base con auditoría
migrations/ // una migración por cambio de esquema
docs/ // estado-actual.md: bitácora de cambios

Capas con una sola responsabilidad

Controlador recibe y valida, servicio decide, repositorio persiste. Sin lógica de negocio en controladores ni componentes de página.

Lógica pura aislada

Generador de listas, parser de acordes, armado de mensajes de WhatsApp y estados de lista son funciones puras en lib/, testeables sin UI.

Contrato explícito

DTOs con class‑validator en la API y tipos compartidos en el cliente; los permisos son strings tipados recurso:acción.

Fase 3 · Diseño

Decisiones técnicas

Qué se eligió, por qué y qué se resignó. Formato de registro de decisiones (ADR).

Contexto

Los líderes querían decidir quién ve qué sin pedir cambios al desarrollador.

Decisión

Permisos recurso:acción persistidos por rol y leídos de la BD en cada petición; roles editables desde la UI.

Consecuencias

Cambios inmediatos y sin redeploy. Costo: una consulta extra por request, mitigada con índices.

Contexto

Se necesitaba una API reutilizable y un cliente con buena carga inicial en celulares.

Decisión

Frontend con SSR (TanStack Start) y backend REST independiente.

Consecuencias

Despliegues y escalado independientes; hay que manejar CORS y el arranque en frío del plan gratuito.

Contexto

El plan gratuito duerme la API y el primer ingreso del domingo tardaba medio minuto.

Decisión

Ping periódico externo más un workflow de GitHub Actions que despierta la API antes del culto.

Consecuencias

Costo cero y experiencia fluida. Dependencia de un servicio gratuito, documentada.

Contexto

Los audios pesan y no deben ser públicos.

Decisión

Cloudflare R2 (API S3) y URLs firmadas temporales emitidas por el backend.

Consecuencias

Sin costo de egreso y acceso controlado por permisos; el cliente nunca ve las credenciales.

Contexto

Cada músico necesita su tono y el PDF debe respetar el formato.

Decisión

Letra con acordes entre corchetes; un parser puro transpone, alinea y renderiza (pantalla y PDF).

Consecuencias

Edición simple y tests unitarios del parser; los casos borde (acordes con bajo, líneas largas) se cubren con pruebas.

Contexto

Se pidió sugerir listas variadas sin repetir lo reciente.

Decisión

Algoritmo determinista con pesos por rotación, reglas de temas y canciones enganchadas.

Consecuencias

Resultados explicables, gratuitos y testeables.

Contexto

Producción la usa gente real cada semana.

Decisión

Flujo develop → PR → main con tipos, lint, tests y build como checks requeridos.

Consecuencias

Menos regresiones; el costo es un ciclo de revisión más largo, aceptable para el riesgo.

Fase 4 · Implementación

Tecnologías

Elegidas por productividad, tipado de punta a punta y costo operativo cero.

Frontend

React 19TanStack StartTanStack RouterTanStack QueryTypeScriptVite 8

UI

Tailwind 4shadcn/uiRadixRechartsjsPDFPWA

Backend

NestJS 12TypeORMPassport · JWTbcryptclass‑validatorSWC

Datos

PostgreSQLNeonCloudflare R2S3 SDKMigraciones

Calidad

VitestoxlintESLintPrettierPuppeteer

Infra

VercelRenderGitHub Actionscron‑job.orgRamas protegidas
Fase 4 · Implementación

Módulos del sistema

Capturas reales con datos de demostración inventados. Tocá una imagen para ampliarla.

Inicio

Inicio

Portada con partículas y accesos rápidos. Destaca la lista en la que el músico está asignado.

lista-propia:ver
Canciones

Canciones

Repertorio con búsqueda, filtro por tipo y etiquetas, vista tarjetas o lista y favoritos.

cancion:ver
Filtros

Filtros del repertorio

Combinación de tipo, tema y tono para encontrar la canción justa en segundos.

cancion:ver
Acordes

Acordes

Letra con acordes alineados, transposición de tono y tamaño de letra, con descarga a PDF.

cancion:ver
Solo acordes

Modo “solo acordes”

Reduce la canción a su progresión y repeticiones, pensado para tocar leyendo de un vistazo.

cancion:ver
Transportador

Transportador de tono

Cambia de tono toda la canción conservando la forma de los acordes con bajo.

cancion:ver
Letras

Letras

Lectura limpia para cantar, con botón de reproducir y salto directo a la canción elegida.

cancion:ver
Reproductor

Reproductor

Audio propio o YouTube, cola, repetir tramo y controles con efecto glass.

escuchar:ver
Listas

Listas

Listas por fecha con estado, equipo asignado y duración total (sin sumar la ofrenda).

lista:ver
Detalle de lista

Detalle de la lista

Secciones, tono por canción, equipo por categorías y copia directa a WhatsApp.

lista:ver
Nueva lista

Nueva lista

Asistente con fecha, título automático, generación aleatoria y plantillas predefinidas.

lista:crear
Orden de la lista

Ordenar la lista

Reordenamiento y asignación de sección de cada canción, con duración recalculada al instante.

lista:editar
Equipo

Equipo

Músicos con sus instrumentos; solo se ofrecen para asignar quienes pueden tocarlos.

usuario:ver
Roles

Equipos y roles

Edición visual de roles y permisos por recurso y acción. Cambios efectivos al instante.

rol:editar
Estadísticas

Estadísticas

Canciones más tocadas, rotación y uso por mes.

estadistica:ver
Gráficos

Gráficos e historial

Visualización con Recharts y búsqueda de una canción para traer las listas donde se tocó.

estadistica:ver
Favoritos

Favoritos

Atajo personal a las canciones que más se usan.

favorito:crear
Instalar la app

Instalación como app

Guía paso a paso para Android e iPhone: la PWA se agrega a la pantalla de inicio.

PWA
Ingreso

Ingreso

Acceso con cuenta o como invitado de solo lectura.

auth

En el celular

Probado en anchos de 320 a 430 px con un script automatizado que mide desbordes.

Inicio móvil
Inicio
Canciones móvil
Canciones
Acordes móvil
Acordes
Listas móvil
Listas
Lista móvil
Detalle
Reproductor móvil
Reproductor
Fase 4 · Implementación

Problemas encontrados y cómo se resolvieron

Los que más enseñaron. Filtrá por área.

Problema

La API gratuita se dormía y el primer ingreso del domingo tardaba cerca de 30 s.

→

Solución

Ping externo periódico más un workflow programado que la despierta antes del culto.

Problema

En iPhone, al enfocar un campo Safari hacía zoom y la pantalla quedaba desplazada.

→

Solución

Inputs a 16 px en dispositivos táctiles y un detector automatizado de desbordes a 320–430 px.

Problema

Un acorde con bajo como A/C# se partía al saltar de línea y se leía mal.

→

Solución

El parser trata el acorde como una unidad indivisible y ajusta el ancho al envolver; cubierto con tests.

Problema

Un texto entre corchetes en la letra se interpretaba como acorde y rompía la alineación.

→

Solución

Validación del token contra la gramática de acordes: lo que no lo es se conserva como texto.

Problema

La duración de la lista sumaba la ofrenda y no coincidía con lo que el equipo tocaba.

→

Solución

Regla de negocio explícita: se usa la primera pista subida, si no hay, la duración del video; la ofrenda no suma.

Problema

El reproductor fijo tapaba las anotaciones y el contenido en tablet.

→

Solución

Reserva de espacio inferior dinámica y barra lateral que se pliega en tablet.

Problema

Cambiar la fecha de una lista dejaba un título desactualizado.

→

Solución

Título derivado de la fecha mientras no haya sido editado a mano.

Problema

Los roles tenían permisos fijos y cada ajuste requería desplegar.

→

Solución

RBAC por permisos en base de datos y pantalla de edición de roles.

Problema

Un lint fallaba en CI solo por saltos de línea de Windows.

→

Solución

Regla de fin de línea unificada y verificación local previa al PR.

Problema

Los videos de YouTube mostraban bordes negros según el formato.

→

Solución

Portada con mejor resolución y recorte adaptado a la relación de aspecto del video.

Cómo se trabajó

Evolución del producto

Base

Autenticación, canciones, listas y primer reproductor. Modelo relacional y migraciones.

Permisos

RBAC granular editable, acceso de invitado y vistas según rol.

Experiencia en ensayo

Acordes con transposición, solo‑acordes, PDF, repetir tramo y letra a tamaño ajustable.

Flujo del líder

Secciones, generador de listas, equipo por instrumento, notas y copia a WhatsApp.

Pulido y calidad

Revisión de tamaños de celular, estilo glass y dorado, CI obligatorio y bitácora de cada cambio.

Hablemos

¿Querés ver el código o probarlo?

Diseñado, desarrollado, desplegado y mantenido de punta a punta.