Arquitectura del stack
Cómo se comunican los componentes del stack mseller — UI web, apps móviles, integraciones externas y backend.
El stack mseller tiene varios componentes que se comunican sobre HTTP
y Firebase. Cada API en mseller-api está hecha para un consumidor
específico:
Roles de cada repo
mseller-api (.NET 10)
Tres APIs en una solución, una por tipo de consumidor:
- Portal.Api (puerto 5186) — Endpoints administrativos para la UI
web. Consumidor único:
mseller-cloud(Next.js). Cubre catálogos, reportes, configuración del tenant, gestión de usuarios. - Consumo.Api (puerto 5188) — Endpoints para las apps móviles que
usa la operación en campo:
mobile-seller(iOS, Swift) — vendedores tomando pedidos en ruta.mseller-lite(React Native / Expo) — almacén: picking de pedidos y carga de camiones.
- Ingestion.Api (puerto 5187) — Endpoints para integraciones de sistemas externos (SAP, otros ERPs, cargas batch). Recibe data de upstream y la sincroniza con Postgres y, cuando aplica, con Firestore.
Persistencia: PostgreSQL (multi-tenant por businessId — un filtro
global en AppDbContext se asegura de que cada query solo vea la data
del tenant del usuario autenticado).
Autenticación: Firebase Auth (verifica los tokens JWT que emite mseller-firebase, ya sea desde producción o desde el emulador local).
mseller-firebase (Cloud Functions)
- Funciones HTTP/callable para sign-up, perfil de usuario, generación de PDFs, integraciones de email/WhatsApp.
- Reglas de Firestore y Storage.
- Configuración de los emuladores que el resto del stack usa localmente.
Las funciones están en TypeScript (Node 22) y se despliegan automáticamente
al hacer merge a main vía GitHub Actions, autenticándose con una cuenta
de servicio dedicada (github-deploy).
mseller-cloud (Next.js)
UI administrativa. Habla con:
- mseller-api vía REST (variable de entorno
TARGET). - mseller-firebase vía
httpsCallable(Firebase Functions SDK). - Firestore directamente para algunas lecturas en tiempo real.
Estilo y componentes: MUI v5 + tema Materio. Estado: Redux Toolkit.
Apps móviles
Las apps móviles no están en el web stack — viven en repos separados y solo necesitas clonarlas si vas a tocarlas.
mobile-seller (iOS, Swift)
App nativa (Swift 5 + Objective-C) para los vendedores en ruta —
toma de pedidos, cobros, facturación, inventario, y demás operaciones
de campo. Consume Consumo.Api con Firebase Auth en el header
Authorization: Bearer {token}. Para trabajar aquí necesitas macOS +
Xcode 16.3 + CocoaPods.
mseller-lite (React Native / Expo)
App de almacén — flujo de preparación (picking de pedidos) y
carga de camión. Conecta a Consumo.Api con axios y JWT de
Firebase, igual que la iOS. Es online-only para los screens de O2D
(no persiste esos flujos en AsyncStorage).
Integraciones externas
Sistemas como SAP y otros ERPs no usan el Portal ni el Consumo —
empujan data al stack vía Ingestion.Api (cargas batch). La
autenticación ahí es por API key (ApiKeyAuthFilter), no con
JWT de Firebase: el sistema externo tiene una llave por tenant, no
una sesión de usuario.
mseller-docs (este repo)
Documentación interna. Built con Fumadocs sobre Next.js, deployado en Vercel.
mseller-marketing-site
Sitio público de marketing en mseller.app — landing,
features, pricing, etc. Es Next.js 14 + Tailwind, deployado en Vercel.
No habla con mseller-api ni con Firebase: es estático/contenido
público. Los CTAs ("Iniciar sesión", "Probar la app") apuntan a
cloud.mseller.app.
Identidad y multi-tenancy
Cada business (tenant) tiene un UUID único compartido entre los tres
sistemas:
- Firebase Auth — el usuario tiene un custom claim
business: <id>. - Firestore — colección
business/<id>con la configuración del tenant. - PostgreSQL — todas las tablas tenant-scope tienen una columna
BusinessIdfiltrada automáticamente porAppDbContext.
Cuando los tres están en sincronía, todo funciona. Cuando se desfasan (por ejemplo, cuando se borra el usuario en Firebase pero queda data en Postgres), se generan tenants huérfanos.
Por esto el seed de desarrollo usa un businessId fijo:
00000000-0000-0000-0000-000000000001. Si reseteas el emulador y vuelves
a sembrarlo, el ID es el mismo, así que cualquier data en Postgres
asociada a ese tenant sigue siendo accesible.
Despliegue (alto nivel)
| Repo | Entorno | Cómo se despliega |
|---|---|---|
| mseller-api | Azure App Service (3 apps) | GitHub Actions, post-merge a main, azure/webapps-deploy |
| mseller-firebase | Firebase (Cloud Functions, Firestore rules, Storage rules) | GitHub Actions, firebase deploy --only functions |
| mseller-cloud | Vercel | Auto-deploy en cada push a main (config en vercel.json) |
| mseller-marketing-site | Vercel | Auto-deploy en cada push a main |
| mseller-docs | Vercel | Auto-deploy en cada push a main |
Cada deploy usa una cuenta de servicio dedicada con permisos mínimos. No se guardan llaves en laptops de developers.
Dominios de producción
Lo que ven los clientes (y dónde apunta cada cosa cuando hablamos de
producción, no del stack local). Cutover a Container Apps + custom
domains terminó el 2026-05-15 — los hostnames de producción están en
mseller.app con TLS gestionado por Azure (gratis, auto-renew).
| Dominio | Repo | Qué sirve |
|---|---|---|
mseller.app | mseller-marketing-site | Landing público — features, pricing, "iniciar sesión". |
cloud.mseller.app | mseller-cloud | Portal admin web (lo que usa el cliente día a día). |
portal.api.mseller.app | mseller-api · Portal.Api | API REST que consume cloud.mseller.app. |
ingestion.api.mseller.app | mseller-api · Ingestion.Api | API de ingesta — recibe payloads de integraciones (SAP, etc.). |
consumo.api.mseller.app | mseller-api · Consumo.Api | API consumida por la app móvil iOS. |
storage.mseller.app | Firebase / GCS | CDN público de imágenes (logos de tenants, productos, etc.). |
| Cloud Functions | mseller-firebase | URL gestionada por Firebase (*.cloudfunctions.net o *.run.app). El cliente las llama vía httpsCallable, no por URL directa. |
El stack de dev vive en el TLD mseller.dev (DNS en Cloudflare en vez
del cPanel de prod), y cubre tanto los APIs como el portal admin:
| Dominio | Repo | Qué sirve |
|---|---|---|
cloud.mseller.dev | mseller-cloud | Portal admin de dev (Vercel). |
portal.api.mseller.dev | mseller-api | Portal.Api dev. |
ingestion.api.mseller.dev | mseller-api | Ingestion.Api dev. |
consumo.api.mseller.dev | mseller-api | Consumo.Api dev (móvil iOS dev). |
Cookies y sesiones no cruzan entre mseller.app y mseller.dev (son
TLDs distintos), así que el SSO del portal de docs / prod cloud se
mantiene aislado del portal de dev — intencional.
Las URLs internas de Azure (mseller-prod-portal-api.mangoocean-fef986ad.eastus2.azurecontainerapps.io,
etc.) siguen funcionando pero no son las que tienen que usar los
clientes — protegen contra lock-in al proveedor. Si alguna integración
todavía tiene hardcoded esas URLs, migrala al hostname con dominio
propio del entorno correspondiente (*.mseller.app para prod,
*.mseller.dev para dev).
Detalles sobre cómo está implementado el DNS, cómo agregar un hostname nuevo, y troubleshooting de TLS: ver Despliegue → Dominios personalizados.
Lectura recomendada
mseller-firebase/docs/EMULATORS.md— la guía canónica de cómo se conectan los emuladores.mseller-api/dev-stack/README.md— la orquestación local.