🏋️ Arquitectura & Decisión de Stack

Elegir el backend — y construir la app para una cadena de gimnasios

Lenguaje, framework y base de datos: qué elegimos y por qué. Luego, un caso real — una app móvil + panel + backend para una cadena de gimnasios en Costa Rica, conectada a los módulos de ERP 2026 (contabilidad, POS, CRM) y a su motor de workflows.

🗣️ TypeScript 🧩 Next.js + Drizzle 🐘 PostgreSQL 🔌 REST + HMAC ⚙️ Workflow Engine
· Pura Vida 🇨🇷 ·
0

El punto de partida

El ecosistema que analizamos antes de decidir

Hoy todo vive en PHP / Laravel: erp2026 (el ERP multi-tenant, autoridad del motor de workflows) publica un paquete Composer que dsf3 consume; ambos sirven a las apps (Expo + CRMs) por una única API REST. La pregunta: ¿qué stack usar para algo nuevo que conviva con esto?

🏢erp2026

Laravel 12 · MySQL · 666 migraciones. ERP multi-tenant (contabilidad, RRHH, POS, CRM). Autoridad del motor de workflows (paquete Composer).

⚖️dsf3

Laravel 12 · MySQL · 385 migraciones. API legal de DSF. composer require el motor + sincroniza por webhooks HMAC.

📱appmovildsf

Expo RN + Web. Cliente puro — REST + Bearer + Pusher + push. Mismo contrato que el CRM Next.js.

1

🗣️ Lenguaje

Rápido · seguro en memoria · buena comunidad — y que cargue una UI

Lenguaje⚡ Velocidad🛡️ Mem-safe 👥 Comunidad🎨 UI🐘 Postgres🏁 Veredicto — razones
TypeScript ★★★★ ★★★★★ ★★★★★ ★★★★★ Mismo lenguaje que tu UI → un solo tipo para pantallas + API, errores atrapados en compilación. Ecosistema más grande, ya corrés Next 16. "Suficientemente rápido" para trabajo I/O-bound.
Go ★★★★★ ★★★★ ★★★★★ ★★★★★ Mejor velocidad-por-esfuerzo + binario único → ideal para un servicio headless. Pierde el type-sharing con la UI; lenguaje nuevo para el equipo.
Elixir ★★★★ ★★★★★ ★★★★ ★★★★★ Realtime imbatible (Channels puede reemplazar Pusher). Paradigma nuevo fuera de tu stack; solo si es realtime-first.
C# / .NET ★★★★★ ★★★★ ★★★★★ ★★★★ Baterías más parecidas a Laravel (EF Core) + muy rápido. Solo si el equipo ya sabe C#; Blazor queda fuera de tu mundo React.
Rust ★★★★★ no-GC ★★★★★ ★★★★★ ★★★★ El más rápido, sin GC — pero curva empinada, ecosistema web delgado, sin historia de UI. Solo ante un techo de performance que no tenés.
PHP / Laravel ★★★★★ ★★★★★ ★★★★ ★★★★ El único que puede hospedar el motor de workflows de erp2026 en-proceso (composer require). Elegilo si el servicio debe correr ese motor.

🐘 PG = PostgreSQL (el elefante es su mascota) · 👥 = tamaño de comunidad/ecosistema · ⚡ = velocidad · 🛡️ = seguro en memoria · 🎨 = qué tan bien sirve una UI.

2

🧩 Framework

Dentro de TypeScript (el lenguaje elegido) + Laravel como excepción

Framework (lenguaje)🔋 Baterías🎨 UI 👥🏁 Veredicto — razones
Next.js 16 + Drizzle (TS) ✋ Mínimas (sumás ORM/queue/auth) ★★★★★ ★★★★ ★★★★★ Igual que tu CRM (cero framework nuevo), UI + API en un solo deploy, tipos end-to-end.
AdonisJS 6 (TS) ✅✅ Estilo Laravel (ORM, migraciones, colas, auth) ★★★★ ★★★★ ★★★★★ El feeling de Laravel en TypeScript — el salto más fácil para tu equipo; comunidad menor es el costo.
NestJS (TS) ◑ Vía módulos solo backend → UI aparte ★★★★ ★★★★ API empresarial estructurada; más ceremonia de la necesaria acá.
Hono + tRPC (TS) ✋ Mínimas UI aparte, tipada ★★★★★ ★★★★ Liviano, rápido, edge-ready; elegilo si es más API que UI.
Laravel (PHP) ✅✅ Todo ★★★★ ★★★★★ ★★★★★ El único que hospeda el motor de erp2026 en-proceso; elegilo si eso es requisito.
3

🗄️ Base de datos

El servicio nuevo tiene su propia DB — libre de elegir la mejor

Motor⚡ Velocidad (tu carga)🔧 Capacidades⚠️ Cuidado🏁 Veredicto
🐘 PostgreSQL Empate en lecturas simples, más rápido en JSONB / full-text / joins / time-series Particionado nativo · JSONB · pg_trgm · FK hacia tablas particionadas Necesita un connection pooler ELEGIR Mejor en tus lecturas y para cachear payloads del ERP en JSONB; resuelve tus hacks de partición/FK/ngram.
🐬 MySQL 8 Empate en simples; algo mejor en conexiones baratas Funciona; full-text ngram; límite de FK en particiones Particionado más torpe, JSON re-parseado SOLO SI el servicio es Laravel y querés paridad de esquema con el ERP.
🦭 MariaDB 11 ≈ MySQL Sin ngram FTS, mismo límite de FK La búsqueda se degrada EVITAR Paridad de velocidad con pérdida de capacidad.

El ERP corre MySQL, pero eso no condiciona tu DB: se integran por la API, no compartiendo base. JSONB es ideal para guardar/consultar los payloads de webhook del ERP que cachees localmente.

4

🏆 Nuestra elección

Menor riesgo · mayor velocidad · una clase entera de bugs vuelta imposible

Top Pick

TypeScript · Next.js 16 + Drizzle · PostgreSQL

🗣️ Lenguaje
TypeScript
Mismo idioma que la UI → tipos compartidos
🧩 Framework
Next.js + Drizzle
Igual que tu CRM, UI + API en un deploy
🗄️ Base de datos
PostgreSQL
JSONB para cachear datos del ERP
🔌 Integración ERP
REST + HMAC
El motor del ERP es invisible al otro lado
⚙️ Workflows
Delegar a Laravel
Disparar el motor por HTTP, no reimplementarlo
🔁 Lo único que cambia la respuesta: si el servicio debe hospedar el motor de workflows dentro de sí (composer require), entonces ese servicio se hace en Laravel + PostgreSQL. Fuera de eso → TypeScript.
5

⚙️ El motor de workflows — cómo funciona con tu app

Un motor de reglas "cuando pasa X → ejecutá acciones"

Evento

El disparador. Ej: weight_goal_reached.

🔎Condiciones

Filtros sobre el evento. Ej: total_kg ≥ 10000.

🎬Acciones

Qué corre: crear tarea · enviar mensaje · emit_event · llamar tu webhook.

La restricción clave: el motor es un paquete Composer (PHP) → tu app Next.js no puede importarlo. Siempre vive dentro de un host Laravel (erp2026). Tu app lo usa por la red: emite eventos, expone acciones (webhooks) y escucha resultados (Pusher). Las reglas se editan en erp2026, no en tu app.
6

🏋️ Caso real — Cadena de gimnasios en Costa Rica

App móvil + panel + backend, conectados a ERP 2026

El cliente tiene una cadena de gimnasios. Eso calza perfecto con el modelo multi-tenant de erp2026: la cadena = un grupo, y cada sucursal = una empresa (company). La contabilidad es por sucursal; los reportes consolidados, a nivel de grupo.

📱App móvil (miembros)

Expo / React Native — los socios registran entrenamientos, ven progreso, su membresía y notificaciones. Igual que tu expertise en appmovildsf.

🖥️Panel web (staff)

Next.js — el personal gestiona socios, clases, sucursales y ve métricas. El "frontend del backend".

🟢Backend del producto

TypeScript (Next.js / Hono) + Drizzle — una sola API que sirve a la app y al panel, compartiendo tipos con ambos.

🐘Base de datos

PostgreSQL — socios, entrenamientos, levantamientos, membresías. JSONB para cachear datos del ERP.

① Clientes — TypeScript (tipos compartidos)
📱 App de socios
Expo / React Native
🖥️ Panel del staff
Next.js (web)
mismos tipos · zod / tRPC
② Backend del gimnasio
🟢 API del producto
Next.js / Hono + Drizzle · auth de socios · lógica de negocio
③ Datos propios
🐘 PostgreSQL
socios · levantamientos · membresías · caché ERP (JSONB)
REST + webhooks HMAC · X-Company-ID
④ ERP 2026 — multi-tenant (Laravel · MySQL)
🏢 GRUPO = «Cadena de Gimnasios CR»
cada sucursal es una empresa (company) con su propia contabilidad
📍 San José
company
📍 Heredia
company
📍 Cartago
company
📒 Contabilidad 🛒 POS 👥 CRM 📦 Inventario ⚙️ Workflow Engine (reglas por empresa)

El backend del gimnasio nunca escribe en la base del ERP. Lee/escribe vía su API (con X-Company-ID de la sucursal) y recibe webhooks HMAC del motor.

7

🎁 El workflow de premio

«10.000 KG levantados → 1 mes gratis → asiento en contabilidad»

1
El socio levanta. La app registra cada set; el backend acumula el total_kg del socio en PostgreSQL.
2
Cruza los 10.000 KG. El backend detecta el cruce (no "≥" en cada set) y emite weight_goal_reached {socio_id, company_id} + firma HMAC → al host Laravel.
3
El motor evalúa la regla (alcance: esa sucursal): total_kg ≥ 10000 ✓ — y dispara las acciones.
4
Acción A — membresía. El motor llama el webhook del backend POST /api/actions/grant-membership → se crea 1 mes gratis en la DB del gimnasio.
5
Acción B — contabilidad. El motor (corriendo dentro de erp2026) registra el comp en la Contabilidad de esa sucursal en-proceso (factura $0 / gasto promocional, según tu contador).
6
Resultado al socio. erp2026 emite por Pusher → la app muestra «¡Ganaste un mes gratis! 🎉».
✅ Lo bueno: el umbral (10.000 KG) y el premio (1 mes) se editan en el editor de workflows de erp2026 — sin redeploy. Cambiar a 15.000 KG o 2 meses es un clic del staff.
⚠️ Cuidá dos cosas: (1) nada de escrituras cross-DB — el asiento se crea dentro de erp2026; (2) idempotencia — emití solo en el cruce y clavá la membresía por socio_id+meta para no premiar dos veces.
8

🛒 Integrar un módulo del ERP — el Punto de Venta

Estilo distinto al motor: llamada directa a la API REST

El POS es un caso distinto al motor de workflows. El motor es un paquete (se importa, solo en PHP); el POS — como los otros 20 módulos — es un módulo interno de erp2026, y la única vía es su API REST. Dos estilos de integración:

⚙️Motor de workflows

Eventos · delegás. Tu backend emite un evento y el motor corre las reglas (en Laravel). Lo usaste para el premio de 10.000 KG.

🛒Módulo (POS)

API REST directa · vos llamás. Tu backend hace POST .../pos/sales y el módulo hace el trabajo (IVA, inventario, contabilidad, factura).

DirecciónCómoPara qué
📥 LeerGET /company/pos/products · /pos/sales (rutas ilustrativas, bajo /company/* + X-Company-ID)Mostrar catálogo, precios y ventas en la app o el panel
🧾 Crear ventaPOST /company/pos/sales + Idempotency-KeyRegistrar una compra (membresía, suplemento, day pass) desde la app
📡 Reaccionarwebhook HMAC pos.sale.created / pos.product.updatedActualizar tu estado y avisar a la app por Pusher
Clientes
📱 App socio
compra / consulta
🖥️ Panel staff
ventas por sucursal
Tu producto (TypeScript)
🟢 Gym backend
cobra + arma la venta
🐘 Postgres
cachea catálogo (JSONB) · refresca por pos.product.updated
POST /company/pos/sales · X-Company-ID · Idempotency-Key
erp2026 · módulo POS — hace todo el downstream
🛒 Punto de Venta — sucursal (company)
una sola llamada → el ERP encadena todo, atómicamente
🧮 IVA · CABYS 📦 Inventario −1 📒 Contabilidad 🧾 Factura electrónica (facturero)
◀ webhook HMAC pos.sale.created → recibo

↑ el recibo vuelve a tu backend → marca la membresía en Postgres → la app muestra «Renovada ✅»

✅ Por qué llamás al módulo en vez de reinventarlo: con una llamada, erp2026 calcula el IVA (13% / tarifas reducidas), aplica CABYS, descuenta inventario, asienta en Contabilidad, numera el recibo y emite la factura electrónica — todo lo regulado de Costa Rica, todo atómico.
ReglaPor qué
Nunca escribir la DB del POS / inventario / contaerp2026 es dueña de la lógica (IVA, CABYS, factura) — la rompés si escribís directo
🏷️ Scopear por X-Company-IDCada venta pertenece a una sucursal (empresa del grupo)
🔁 Idempotencia al crear ventaUn reintento no puede cobrar / asentar dos veces
🔑 Auth de servicio, no del socioEl gym backend es un servicio de confianza de la empresa (app-token + X-Company-ID); los socios no son usuarios del ERP
🎯 En una línea: el POS no es un paquete — es un módulo del ERP que llamás por API. Tu backend TS registra la venta con POST .../pos/sales (token + X-Company-ID + idempotencia), y erp2026 hace IVA + inventario + contabilidad + factura electrónica y te devuelve un webhook con el recibo.
9

🗺️ Mapa de integración — todo erp2026 ↔ stack nuevo

Cada módulo: ¿paquete, API o evento?

Hay tres formas de tocar erp2026. Solo dos cosas son paquetes (y son PHP); todo lo demás se consume por la red — así que tu servicio TypeScript puede usar todo erp2026, siempre por API o eventos.

📦Paquete (Composer)

Solo PHP puede importarlo. Desde TS → delegás a un host Laravel. Solo Workflows y Gamificación.

🔌API REST

Cualquier lenguaje. Llamás /company/* con X-Company-ID + token. La mayoría de los módulos.

📡Evento / Webhook

Cualquier lenguaje. Pusher (realtime) + webhooks HMAC (resultados del motor / módulos).

Capacidad de erp2026Se distribuye comoTu backend TS lo usa por…En concreto
⚙️ Motor de Workflows 📦 Paquete · workflows-dist 📡 Eventos (delegás) Emitís el evento → el motor corre en Laravel y te llama de vuelta
🏆 Gamificación 📦 Paquete · gamification-dist 🔌 API📡 eventos Llamás «otorgar / leer puntos»; corre en Laravel
📒 Contabilidad Módulo interno 🔌 API REST Asientos / balances vía /company/... (scope sucursal)
🛒 Punto de Venta Módulo interno 🔌 API📡 webhooks POST .../pos/sales → hace IVA / inventario / conta / factura
📦 Inventario Módulo interno 🔌 API📡 webhooks Stock / conteos; se mueve solo desde el POS
👥 CRM Módulo interno 🔌 API📡 webhooks Leads / contactos del socio
🧾 Factura electrónica Vía POS / Conta (facturero) ↪ Indirecto No la llamás: la dispara la venta del POS
🔔 Realtime Compartido (Pusher) 📡 Pusher SDK Canales privados + eventos a la app
🔐 Auth Sanctum + app-token 🔑 Token de servicio El backend es servicio de confianza + X-Company-ID
🧭 La regla universal: solo 2 capacidades son paquetes (PHP-only): Workflows y Gamificación. Todo lo demás es API REST + eventos — así que un servicio TypeScript consume el 100% de erp2026 por la red, sin tocar su base de datos nunca.
10

🧭 Cómo lo construimos — resumen

Tres movimientos para conectar el gimnasio al ERP

1
Onboard del gimnasio en erp2026. Crear el grupo (la cadena) y una empresa por sucursal; activar módulos (📒 Contabilidad, 🛒 POS, 👥 CRM); usuarios/roles; emitir credenciales + X-Company-ID.
2
Construir su producto. App móvil (Expo) + panel web (Next.js) sobre un backend TypeScript + Drizzle → PostgreSQL, con tipos compartidos entre los tres.
3
Conectar — por company_id. El backend llama la API del ERP (X-Company-ID + token) y recibe webhooks HMAC; los workflows (como el premio de 10.000 KG) corren en el motor de erp2026, alcance por sucursal.
🎯 En una línea: App móvil (Expo) + panel (Next.js) sobre un backend TypeScript + PostgreSQL que conversa con erp2026 por REST + webhooks HMAC — la cadena es un grupo, cada sucursal una empresa, la contabilidad y los workflows viven en el ERP, y tu app nunca toca su base directamente.