Propuesta de cómo podemos construir llamaxmi
Una propuesta de stack a debate, pieza por pieza, con el porqué de cada elección y alternativas reales en cada punto para decidir en equipo.
LA PROPUESTA, EN UNA FRASE
Un solo proyecto: Next.js todo-en-uno
Next.js (web + app + admin + API, en un proyecto) + TypeScript + Tailwind 4 + shadcn · PostgreSQL + Drizzle + Better‑Auth · worker Node para las llamadas · VPS propio + Dokploy · Rust para el motor de voz más adelante.
Tras analizarlo, la recomendación cambia: el proyecto quiere ser pro y todo-en-uno, sin tener que migrar dentro de unos meses. La velocidad de montaje no es la prioridad — lo es no arrepentirnos. Por eso gana Next.js frente a la vía anterior (Astro para la web + React/Vite para la app, en proyectos separados). Lo único que se mantiene aparte, con razón, es el worker que ejecuta las llamadas. La decisión gorda que sigue abierta —montar todo en nuestro VPS o tirar de Supabase— se cierra el jueves.
CÓMO ENCAJA TODO
Un proyecto, más un worker aparte para las llamadas
Ya no son tres piezas separadas. La web pública, la app privada, el panel de admin y la API son páginas de la misma web, en un solo proyecto Next.js. Fuera de él queda solo el worker que ejecuta las llamadas — porque un proceso largo de audio no cabe dentro de la petición de una página.
Regla de oro: web, app y admin son la misma web (un solo Next.js). Lo único que vive fuera es el worker/motor de voz — todo lo demás está en un proyecto.
LA DECISIÓN DE LA APP
Next.js, no dos proyectos separados
La pregunta ya no es «qué framework para la app», sino «un proyecto o dos». La vía anterior partía la web (Astro) y la app (React + Vite) en dos proyectos separados. Next.js las une en uno: web pública, app privada, admin y API bajo el mismo techo. Para un producto que quiere ser pro y no migrar en unos meses, unificar gana.
| Criterio | Next.js (un proyecto) | Astro + React/Vite (separado) | Gana |
|---|---|---|---|
| Todo en uno (web + app + admin + API) | Un repo, un despliegue | Dos proyectos, dos despliegues | Next.js |
| SEO | Excelente + indexa páginas dinámicas (p.ej. /negocios/…) | Bueno solo en la landing; la app (SPA) no indexa | Next.js |
| Login compartido | Uno solo, mismo proyecto | Partido entre dos apps | Next.js |
| Componentes | Compartidos entre web y app | Duplicados en cada proyecto, para siempre | Next.js |
| Despliegue | Uno, sin CORS interno | Dos, con frontera y CORS entre ellos | Next.js |
| Riesgo de migrar en el futuro | Ya es el destino: no se migra | Alto — el día que la app necesite SEO o previews, toca migrar a Next | Next.js |
| Web 100% estática pura | Muy rápida (diferencia marginal) | Un pelín más rápida en páginas estáticas | Separado (marginal) |
| Control fino / desacople a gran escala | Menos separado por diseño | Mayor desacople y control fino | Separado (a gran escala) |
Siendo honestos con la vía separada: la web estática de Astro es un pelín más rápida en páginas 100% estáticas (diferencia marginal, no se nota en Google); separar da mayor desacople y control fino (útil a gran escala, no ahora); y React + Vite «a pelo» tiene menos magia que el App Router de Next (menos cosas que aprender), pero a cambio no unifica nada. El coste permanente de separar —dos proyectos, dos despliegues, autenticación partida, CORS y componentes duplicados para siempre— pesa más que ese beneficio pequeño.
El riesgo de migrar lo tiene la opción separada, no Next. Con Astro+Vite, el día que necesites SEO en páginas de la app, enlaces compartibles con vista previa (WhatsApp) o SSR dinámico, con Vite no puedes y tendrías que migrar a Next. Con Next ya lo tienes.
CÓMO ENCAJAN FRONT Y BACK
El front y la API viven en el mismo proyecto
Con Next.js no hay dos servicios que se hablen para el CRUD: es un solo proyecto.
Las pantallas y la API viven en el mismo Next.js: la lógica de datos son Route Handlers dentro del proyecto. No hay dos servicios cruzando una frontera para leer y escribir en la base de datos — no hay CORS interno ni «app llama a un backend aparte» para el CRUD.
- Next.js hace las pantallas y la API en la misma base de código. Para el CRUD no hay frontera que cruzar.
- La frontera clara queda solo con el worker: la web le encola trabajos de llamada y el worker los ejecuta aparte.
- El motor de voz en Rust (fase 3) habla con el worker por esa misma frontera — el resto del sistema no se toca.
La separación se guarda para donde importa: los procesos largos de audio no caben en la petición de una página, así que el worker vive fuera. Todo lo demás está unido.
OPCIÓN A · MONTARLO NOSOTROS
Todo en nuestro VPS, controlado por nosotros
Con el framework ya decidido (todo en un Next.js), esta opción es sobre quién aloja la base de datos y el login: los montamos nosotros mismos, en nuestro VPS. La API vive dentro del propio Next.js (Route Handlers); nosotros ponemos el PostgreSQL y el auth. Más control, cero dependencia de plataformas externas, y todo vive en un sitio que es nuestro. (La otra vía, Supabase, está más abajo — Opción B.)
Las piezas que necesitamos
| Para qué | Qué usamos |
|---|---|
| Servidor / API | Next.js + TypeScript · Route Handlers (dentro del mismo proyecto) |
| Base de datos | PostgreSQL en el VPS |
| Hablar con la BD (con tipos) | Drizzle ORM + Drizzle Kit (migraciones) |
| Login de usuarios (auth) | Better‑Auth o Lucia (TypeScript, se integran con nuestro Postgres) · Auth.js también vale |
| Contraseñas y sesiones | argon2 (hash) + JWT (jose) |
| Estado en vivo (los 9 estados) | WebSocket propio: Socket.IO (reconexión y salas de serie) |
| Validar lo que entra por la API | Zod |
| Colas de trabajo (resúmenes, reintentos) | Tabla jobs en Postgres al principio · BullMQ + Redis si escala |
| Logs | Pino |
Lo que asumimos al hacerlo nosotros
Montar el servidor propio significa que el login, el tiempo real, los backups y el mantenimiento del servidor son trabajo nuestro — justo lo que una plataforma como Supabase da hecho. No es más difícil, son más piezas que mantenemos. A cambio: control total y sin atarnos a nadie.
CÓMO SE PUBLICA
Despliegue con GitHub + Dokploy
Todo se despliega solo desde GitHub: subes el código y se publica. Para gestionarlo en nuestro VPS usamos Dokploy — una herramienta open-source que se instala en el servidor y hace de «panel de control» tipo Vercel/Heroku, pero en nuestra máquina.
| Con Dokploy tenemos… | Sin salir de nuestro VPS |
|---|---|
| Despliegue automático al hacer push a GitHub | Sí |
| La base de datos PostgreSQL (un clic) | Sí, gestionada por Dokploy |
| HTTPS automático (certificados) | Sí (Traefik por debajo) |
| Backups programados de la BD | Sí |
| Ver logs, reiniciar, variables de entorno | Sí, desde su panel |
En una frase: Dokploy nos da la comodidad de Vercel/Heroku pero en nuestro propio servidor. Empujas a GitHub, se despliega solo, y la base de datos, el HTTPS y los backups los gestiona él — sin renunciar a que todo sea nuestro.
OPCIÓN B · LA VÍA RÁPIDA
Supabase, si queremos ir más rápido
Un atajo válido: Supabase es PostgreSQL, con lo demás ya montado.
Supabase no es otra base de datos: por dentro es el mismo PostgreSQL, pero con el login, el tiempo real y los permisos ya hechos. Es la vía rápida — nos ahorra justo las piezas que en nuestro stack montamos a mano.
| Pieza | Nuestro VPS | Supabase |
|---|---|---|
| Base de datos | La montamos y gestionamos | El mismo Postgres, gestionado |
| Login | Lo montamos (Better-Auth/Lucia) | Incluido |
| Tiempo real | WebSocket propio (Socket.IO) | Incluido (Realtime) |
| Backups y servidor | Nuestros (vía Dokploy) | Gestionado |
| Control / dependencia | Total, cero dependencia | Alto, en su plataforma |
Cuándo tiraría de Supabase: si en fase 1 queremos llegar rapidísimo sin dedicar tiempo a montar login y tiempo real. Y como por debajo es Postgres estándar, migrar a nuestro VPS más tarde es llevarse la base de datos tal cual — sin quedarnos atrapados. Nota: Supabase también se puede auto-alojar en nuestro propio VPS (es open-source), un punto intermedio entre las dos opciones.
La decisión que queda abierta (jueves): ¿montamos todo nosotros en el VPS (más control, más trabajo inicial) o arrancamos con Supabase (más rápido, menos control)? El diseño y el frontend son idénticos en ambos casos.
¿HAY ALGO MEJOR?
Otras vías que valoramos
Ejercicio honesto: además de la vía elegida (Next.js todo-en-uno), estas son las opciones que estuvieron sobre la mesa.
Astro + React/Vite separado (evaluada y descartada)
▲ Mejora: la web estática de Astro es un pelín más rápida en páginas 100% estáticas (diferencia marginal, no se nota en Google); separar da mayor desacople y control fino (útil a gran escala); y React + Vite «a pelo» tiene menos magia que el App Router de Next (menos cosas que aprender).
▼ Cuesta: dos proyectos, dos despliegues, autenticación partida, CORS y componentes duplicados para siempre. Y el riesgo de migrar lo tiene ella: el día que la app necesite SEO, previews compartibles o SSR dinámico, con Vite no puedes y toca migrar a Next.
Era la propuesta de partida; la descartamos porque cambia un beneficio pequeño (SEO de la landing) por complejidad permanente. Con Next.js ya tenemos todo eso sin partir nada.
Elixir / Phoenix
▲ Mejora: su tecnología nació literalmente para telefonía; un proceso por llamada con recuperación automática si algo falla; el tiempo real viene de serie. Conceptualmente, el encaje más elegante.
▼ Cuesta: nadie del equipo lo conoce, contratar es difícil en España, y su superpoder queda desaprovechado si solo orquestamos un proveedor externo.
Lo elegiría si construyéramos el propio motor de voz, o si hubiera un senior de Elixir en el equipo.
Supabase-first (la alternativa fácil)
▲ Mejora: login completo sin escribir una línea, base de datos gestionada y tiempo real: la app recibe los 9 estados en vivo sin que montemos nada. Recorta semanas.
▼ Cuesta: dependemos de su plataforma (mitigable: es Postgres estándar, migrable); menos control que en nuestro VPS.
Si en fase 1 queremos velocidad máxima. Detallada arriba en «Opción B · la vía rápida».
Rust para el motor de voz (más adelante)
▲ Mejora: cuando montemos el servidor de la IA de voz, Rust brilla ahí: concurrencia masiva barata, sin fugas de memoria en llamadas largas, latencia predecible.
▼ Cuesta: curva de aprendizaje seria; los SDK de voz son Node-first. No aporta en el CRUD de fase 1.
Como fase posterior, no en el arranque: primero Node + Postgres, y Rust entra solo en el motor de voz cuando toque.
RESUMEN
Lo que propongo, para debatirlo
Esto es mi propuesta de partida — tecnologías con las que estoy cómodo y que encajan bien. Nada está decidido: lo llevo a la reunión para debatirlo en equipo. Cada pieza tiene su alternativa (arriba), así que descartad, cambiad o confirmad lo que veáis.
Mi propuesta, pieza por pieza:
Next.js todo-en-uno (web + app + admin + API) TypeScript Tailwind 4 + shadcn/ui PostgreSQL + Drizzle + Better-Auth Worker Node aparte para las llamadas Rust para el motor de voz (fase posterior)
La decisión que más pesa:
¿Todo en nuestro VPS o arrancar con Supabase?
Quién gestiona la base de datos, el login y el tiempo real: montarlo nosotros en el VPS (más control, más trabajo inicial) o Supabase de atajo (más rápido, dependemos de su plataforma). El framework ya está decidido: todo en un Next.js, con el worker de llamadas aparte y Rust entrando después, solo cuando montemos el motor de voz. El despliegue lo haría desde GitHub con Dokploy — eso también a debate, no es un mandato.
Para el diseño, da igual qué se decida: el frontend y las pantallas son idénticos con cualquiera de las opciones. Esto es del carril de servidor y lo cerramos entre todos.
DETALLE QUE AHORRA UN SUSTO
Un aviso sobre shadcn + Tailwind
La versión actual de la herramienta de shadcn ya no soporta Tailwind 3 (asume Tailwind 4). Como arrancamos un Next.js nuevo desde cero, esto no nos da problema: vamos directos a Tailwind 4 + shadcn@latest, sin fijar versiones ni el lío del pin.
- Nuestro caso (proyecto nuevo): Tailwind 4 desde el día 1 y shadcn@latest sin más. Es lo recomendado al empezar de cero.
- Solo si hubiese que quedarse en Tailwind 3 (no es el caso): habría que fijar shadcn@2.3.0, la última versión compatible. Lo dejamos anotado por si acaso.
Los tokens de color del sistema de diseño ya están en formato shadcn — se integran con un paso de conversión (a canales de color), no «pegando tal cual». Todo el detalle está en ARQUITECTURA.md §3.
EL MOTOR DE VOZ, EN CORTO
Y para la llamada: LiveKit + Gemini
La web no hace la llamada ella misma (una página no puede quedarse minutos hablando por teléfono). La ejecuta un worker aparte que orquesta tres piezas:
El detalle de qué ocurre paso a paso durante la llamada —y sobre todo cómo la IA te pregunta en directo cuando se topa con un límite— está explicado con su diagrama en Producto v2 → «Cómo funciona por dentro». Aquí solo señalamos las piezas.