← Volver al portal
Documentación técnica · DOC-ARQ-01

Arquitectura de Solución

Stack, diagrama de capas, modelo de datos y pipeline CI/CD del marketplace Plazi

Documento
DOC-ARQ-01
Versión
1.0 · jun 2026
Audiencia
CTO · Engineering
Diagramas
5

1 · Stack tecnológico

Las herramientas que mueven Plazi

1.1 · Frontend

CapaTecnologíaVersiónRol
WebNext.js (App Router)15.x · React 19Sitio principal + PWA /app
MobileExpo SDK + React Native52 · RN 0.76App nativa iOS + Android
Routing mobileExpo Router4.xFile-based routing
Design systemTamagui1.112UI compartida web ↔ mobile
Estado servidorTanStack Queryv5Cache + mutations
ValidaciónZod3.23Schemas compartidos
FuentesSora · Instrument Serif · JetBrains MonoGoogle FontsTipografía Plazi

1.2 · Backend y datos

CapaTecnologíaRol
Base de datosSupabase Postgres13 tablas con RLS
AuthSupabase Auth (ES256)OTP por correo + biométrico mobile
StorageSupabase StorageBucket product-images (público con RLS)
API RESTNext.js Route Handlers22 endpoints + Bearer JWT
Server ActionsNext.js Server ComponentsMutations checkout + admin
PushExpo Push APINotificaciones nativas mobile
EmailsResendOTP + confirmaciones de orden

1.3 · Pagos e integraciones

ServicioUso
Wompi (Bancolombia)Nequi · Daviplata · PSE · Tarjeta · Contraentrega
Demo ModeSimulador Wompi para presentaciones (banner naranja activo)
Servientrega / CoordinadoraLogística (configurable por seller)

1.4 · DevOps

CapaServicio
Hosting webVercel (Fluid Compute · Node 24 LTS)
Build mobileEAS Build (Expo Application Services)
OTA updates mobileEAS Update
CI/CDVercel auto-deploy desde GitHub master
Monorepopnpm workspaces + Turborepo
RegistrarPorkbun (dominio plazi.co)
DOC-ARQ-01 · Página 1

2 · Diagrama de capas

Cómo se conecta todo end-to-end

┌─────────────────────────────────────────────────────────────────────┐ │ USUARIO FINAL │ │ 📱 Comprador (mobile/web) 🏪 Vendedor (mobile/web) 🛡 Admin │ └──────────────┬──────────────────────────────┬──────────────────────┘ │ │ ┌───────▼────────┐ ┌────────▼─────────┐ │ APP NATIVA │ │ SITIO WEB + │ │ Expo SDK 52 │ │ PWA Next.js 15 │ │ iOS · Android │ │ (React 19) │ │ │ │ │ │ • Push notifs │ │ • PWA /app │ │ • Cámara │ │ • Panel vendedor │ │ • Biométrico │ │ • Admin │ │ • Share WA │ │ • Checkout │ └────────┬───────┘ └────────┬─────────┘ │ │ │ Bearer JWT (mobile) │ Cookies httpOnly (web) │ ─────────────────► │ ────────────────────► │ │ ┌────────▼──────────────────────────────▼───────────────────┐ │ API REST (Next.js Route Handlers) │ │ │ │ /api/products /api/orders /api/push /api/wishlist │ │ /api/addresses /api/notifications /api/admin/* │ │ │ │ ・ Server Actions (mutations) │ │ ・ Notificaciones inbox + push (best-effort) │ └────────────────────────┬───────────────────────────────────┘ │ │ Service role + RLS │ ┌────────────────────────▼───────────────────────────────────┐ │ SUPABASE (Postgres) │ │ │ │ profiles · sellers · products · product_variants │ │ orders · order_items · reviews · addresses · cities │ │ wishlists · notifications · coupons · outbox_events │ │ │ │ Storage: bucket product-images (público con RLS upload) │ │ Auth: OTP correo (ES256) + sesiones │ └─────────────┬──────────────────────────────┬───────────────┘ │ │ ▼ ▼ ┌──────────────────────────┐ ┌──────────────────────────┐ │ WOMPI (pagos) │ │ RESEND (emails) │ │ Nequi · PSE · Card │ │ OTP · órdenes · push │ └──────────────────────────┘ └──────────────────────────┘

Notas del diagrama

DOC-ARQ-01 · Página 2

3 · Modelo de datos

13 tablas core en Supabase Postgres

TablaPropósitoRelaciones clave
profilesPerfil de usuario (1:1 con auth.users)id = auth.users.id
citiesCatálogo de ciudades CO (Bogotá, Medellín, …)
categories12 categorías de productos seed
sellersTiendas (vendedores)owner_id → profiles
addressesDirecciones guardadas del compradoruser_id → profiles · default único por user
productsCatálogo de productosseller_id · category_slug · city_origin · images JSONB
product_variantsVariantes (precio, inventario)product_id
ordersÓrdenes de comprabuyer_id · address_snapshot JSONB · payment_method JSONB
order_itemsItems por ordenorder_id · seller_id · product_id · variant_id
reviewsReseñas 1-5 estrellasorder_item_id · product_id · buyer_id · seller_id
couponsCupones de descuento (percent / fixed)code único · seller_id (opcional)
wishlistsFavoritos del compradoruser_id · product_id · UNIQUE (user, product)
notificationsInbox de notificacionesrecipient_id · type · data JSONB · read_at
outbox_eventsEvent log (Kafka-ready)aggregate_type · event_type · payload JSONB

Migraciones aplicadas

  1. 20260516120000_init.sql · schema inicial (10 tablas core)
  2. 20260516120100_seed_catalog.sql · seed de ciudades y categorías
  3. 20260520010000_seed_demo_products.sql · 20 productos demo
  4. 20260520195000_order_status_enum_values.sql · enum order status
  5. 20260520200000_reviews_coupons_cancellations.sql · reviews + coupons
  6. 20260606130000_wishlists_notifications.sql · favoritos + inbox
DOC-ARQ-01 · Página 3

4 · Flujo end-to-end de compra

De la búsqueda hasta el pago y la notificación

[1] 🔍 Comprador navega catálogo └─► GET /api/products · SSR con generateMetadata · OG dinámico [2] 🛒 Agrega al carrito (cookie state) └─► server action addToCart() · escribe cookie httpOnly [3] 💳 Checkout /pagar (o mobile /api/orders/place) ├─► verifica auth · hidrata items ├─► calcula subtotal + envío + descuento de cupón (si aplica) ├─► insert en orders + order_items ├─► insert en outbox_events (order.created) ├─► notifySellersNewOrder() · push Expo + inbox notifications └─► sendOrderConfirmationEmail() via Resend (Wompi confirma async) [4] 💰 Wompi procesa el pago └─► webhook → /api/wompi/webhook · actualiza status orden [5] 📦 Vendedor recibe push y nota en inbox └─► abre /(panel)/pedidos/[number] · marca como packing/shipped/delivered [6] 🚚 Status cambia → trigger ├─► insert en outbox_events (order.status_changed) ├─► notifyBuyerOrderStatusInbox() · escribe en notifications ├─► notifyBuyerOrderStatus() · push Expo al comprador └─► sendOrderStatusEmail() via Resend [7] ✅ Comprador recibe push, abre el detalle └─► /order/[number] (mobile) o /app/pedidos/[number] (PWA)
Idempotencia: los triggers de notificación son best-effort (catch silencioso). Si la tabla notifications no existe (migración pendiente), el flujo principal de compra no se interrumpe.
DOC-ARQ-01 · Página 4

5 · Pipeline CI/CD

Del commit a producción

┌──────────────────┐ git push ┌──────────────────┐ │ Desarrollador │ ────────────────►│ GitHub master │ │ (local) │ │ SebasInvent/plazi│ └──────────────────┘ └────────┬─────────┘ │ webhook GitHub │ ▼ ┌────────────────────────────┐ │ Vercel CI/CD │ │ • pnpm install │ │ • turbo build (filter) │ │ • Next.js build │ │ • Deploy preview/prod │ └─────────────┬──────────────┘ │ ┌─────────────▼──────────────┐ │ Producción · Vercel │ │ https://plazi.co │ │ Edge CDN + Fluid Compute │ └────────────────────────────┘ ┌────────────────────────────────────────────────────────────┐ │ Mobile (independiente de Vercel) │ │ │ │ Desarrollador → eas-cli build → EAS Cloud → APK/IPA │ │ ↓ │ │ expo.dev (download) │ │ │ │ OTA: eas-cli update --branch preview → user app updates │ └────────────────────────────────────────────────────────────┘

Ramificación

Comandos clave

# Web
pnpm dev          # arranca local (Next.js dev)
pnpm build        # build de producción
pnpm typecheck    # check de tipos web + mobile

# Mobile
pnpm --filter @plazi/mobile dev    # Expo dev server
pnpm --filter @plazi/mobile test   # 30 smoke tests
eas build --platform android --profile preview   # APK Beta

# Deploy
git push origin master    # Vercel auto-deploy

Observabilidad

DOC-ARQ-01 · Página 5 (final)