Skip to content

PRD Principal — BeautyHub

Documento estratégico. La definición táctica de cada dominio vive en docs/domains/.


1. Visión

Conectar en una sola plataforma a usuarios que buscan servicios de belleza con profesionales y centros que los prestan, ofreciendo a cada lado valor específico:

  • A los usuarios: descubrir, comparar, reservar y reseñar servicios de belleza (salones, estética, uñas, tatuajes, piercing, barberías, maquilladoras, servicios a domicilio) con confianza y en un solo lugar.
  • A los centros: una herramienta completa para administrar su negocio — agenda, servicios, staff, reservas, reputación, pagos — y ganar visibilidad en el mercado local.
  • A los profesionales: construir una trayectoria y reputación propia, portátil entre centros o como independientes a domicilio.

Propuesta de valor diferencial

  1. Reputación portátil del profesional. Las reseñas siguen al profesional, no al centro. Esto premia el talento real y facilita que los centros contraten con evidencia de desempeño.
  2. Modelo dual sin fricción. Un mismo profesional puede trabajar en uno o varios centros y además atender como independiente sin abrir cuentas distintas.
  3. Centro con control granular. El dueño delega permisos específicos a profesionales y colaboradores (recepción, coordinación) desde un panel, sin código ni configuraciones complejas.
  4. Descubrimiento geográfico. Búsqueda tipo Airbnb: mapa + filtros por servicio, sin requerir saber el nombre del salón.

2. Usuarios y personas

Sofía — Usuaria final (Consumer)

28 años, vive en Chapinero, Bogotá. Busca una maquilladora para un evento en 2 semanas. Quiere:

  • Comparar opciones cercanas con reseñas reales
  • Ver precios sin llamar
  • Reservar desde el celular sin crear 10 cuentas
  • Recibir recordatorios para no olvidar la cita

App que usa: Consumer App (React Native)

Carlos — Dueño de centro (Business Owner)

40 años, dueño de "Glamour Studio" con 2 sedes (Chapinero y Usaquén). Administra personalmente la agenda pero pierde tiempo en coordinar citas por WhatsApp. Quiere:

  • Que sus clientes reserven solos
  • Gestionar su agenda y la de su equipo desde un solo lugar
  • Delegar la operación diaria a una recepcionista cuando esté ocupado
  • Medir qué profesional rinde más, qué servicio se vende más

App que usa: Business App (móvil) y Web Admin

María — Profesional independiente/asociada (Professional)

32 años, maquilladora profesional. Los fines de semana trabaja en un salón y entre semana a domicilio. Quiere:

  • Una sola agenda que no se solape
  • Llevarse sus reseñas cuando cambie de centro
  • Cobrar precios distintos según el contexto (domicilio vs salón)

App que usa: Business App (móvil) y Web Admin

Laura — Colaboradora (Collaborator)

24 años, recepcionista en Glamour. No es profesional, pero gestiona la agenda diaria. Quiere:

  • Confirmar/cancelar citas rápido
  • Responder a clientes (futuro: chat)
  • Saber qué puede hacer y qué no sin preguntar a Carlos cada vez

App que usa: Business App (móvil) y Web Admin

Diego — Administrador de plataforma (Platform Admin)

Interno. Supervisa salud de la plataforma, modera contenido, gestiona el catálogo global de servicios, soporta negocios.

App que usa: Web Admin con permisos elevados


3. Modelo de negocio

Monetización

Freemium: la capa gratuita permite operar un negocio básico (agenda, reservas, perfil público). Features avanzados son pagos.

Fuentes de ingreso:

FuenteMomentoDetalle
Comisión por reservaDesde día 1Porcentaje por cada reserva completada. Aplica a centros y a profesionales independientes.
Suscripción premiumFase 6+Nómina, asistente IA, integración Google Reviews, marketing avanzado, WhatsApp Business.
Servicios adicionalesFuturoCampañas destacadas, publicidad pagada dentro de la app, kits de bienvenida con proveedores.

Unit economics (hipótesis inicial)

  • Ticket promedio en Colombia: ~$50.000 COP por servicio
  • Comisión propuesta: entre 5% y 10% por reserva completada
  • Break-even por negocio activo: ~20 reservas mensuales

4. Métricas de éxito

North Star Metric

Reservas completadas por semana — captura valor entregado a ambos lados del marketplace.

KPIs por fase

Adopción:

  • DAU/MAU de Consumer App
  • Número de centros activos (al menos 1 reserva en último mes)
  • Número de profesionales con perfil activo
  • % usuarios con email verificado

Engagement:

  • Reservas por usuario activo/mes
  • Retention D7, D30, D90
  • Reseñas por reserva completada
  • Tiempo desde búsqueda hasta reserva

Negocio:

  • GMV (Gross Merchandise Value) procesado
  • Comisión mensual recurrente
  • Centros que suben de capa gratuita a premium
  • NPS de cada persona (Sofía, Carlos, María, Laura)

Calidad:

  • % reservas completadas sin cancelación
  • Rating promedio de profesionales activos
  • Tasa de disputas/incidencias

5. Roadmap por fases

Cada fase agrega valor entregable de punta a punta. No hay fases "solo de infra".

FaseFocoAlcance resumenEntregable para usuario
0. BaseIdentidad y autorizaciónauth + authz + infraestructura VPS + CI/CD + apps scaffoldedUsuarios se registran y entran
1. CoreCatálogo y agendaaccounts + catalog + schedulingUn negocio puede crear su perfil, servicios y horarios
2. ReservaFlujo completo de bookingbookingUn usuario puede reservar un servicio
3. DiscoveryMapa y búsquedadiscoveryUsuario descubre sin saber a quién buscar
4. ReputaciónReseñas portátilesreviewsProfesionales construyen trayectoria
5. Pagos y comisiónMonetización basebilling (MVP)Plataforma empieza a generar ingresos
6. NotificacionesRetención y UXnotifications (push → email → WhatsApp)Menos no-shows, más engagement
7. PremiumFeatures avanzadosExtensiones de billing + dominios de valor (chat, IA, analítica)Centros pagan por más valor

Nota: Las fases no son estrictamente secuenciales — varios dominios pueden desarrollarse en paralelo por los dos devs una vez auth y authz estén listos.


6. Arquitectura de alto nivel

Stack tecnológico

CapaTecnologíaRazón
BackendGo (modular monolith)Eficiencia memoria+costo, excelente para APIs, sin overhead microservicios en MVP
AutorizaciónOpenFGA (ReBAC)CNCF, inspirado en Zanzibar, evita deuda técnica de RBAC custom
Base de datosPostgreSQL + PostGISGeoespacial nativo para mapa, madurez, multi-tenancy row-level
Caché/colasRedisSesiones, caché de checks de autorización, cola de outbox
Consumer AppReact Native (Expo)OTA updates, iteración rápida, reutilización de talento JS
Business AppReact Native (Expo)Misma razón, equipo pequeño puede mantener ambas
Web AdminReactEcosistema maduro, compatible con código de RN
InfraestructuraVPS con Docker ComposeBajo costo inicial, migración a k8s/cloud cuando lo justifique la escala

Los 10 dominios del backend

┌────────────────────────────────────────────────────┐
│                 Presentación                        │
│  Consumer App   │   Business App   │   Web Admin   │
└────────────────────────────────────────────────────┘

                   API (HTTP/REST)

┌────────────────────────────────────────────────────┐
│                     Backend Go                      │
│                                                     │
│  ┌─────────┐  ┌─────────┐                          │
│  │  auth   │  │  authz  │  ← Fundacionales        │
│  └─────────┘  └─────────┘                          │
│                                                     │
│  ┌──────────┐ ┌─────────┐ ┌────────────┐           │
│  │ accounts │ │ catalog │ │ scheduling │ ← Negocio │
│  └──────────┘ └─────────┘ └────────────┘           │
│                                                     │
│  ┌─────────┐ ┌───────────┐ ┌─────────┐             │
│  │ booking │ │ discovery │ │ reviews │ ← Dinámica  │
│  └─────────┘ └───────────┘ └─────────┘             │
│                                                     │
│  ┌───────────────┐ ┌─────────┐                     │
│  │ notifications │ │ billing │ ← Transversales    │
│  └───────────────┘ └─────────┘                     │
└────────────────────────────────────────────────────┘
         │                             │
    PostgreSQL                    OpenFGA
    + PostGIS                     (binario Go local)

       Redis

Responsabilidad por dominio

#DominioResponsabilidad
01authIdentidad: registro, login, sesiones, OTP, recuperación
02authzAutorización: schema ReBAC, Check, Write, sync outbox
03accountsAccount, Business, Branch, Professional, BranchMembership
04catalogServiceTemplate, ProfessionalService, BranchService, flujo de aprobación
05schedulingBranchSchedule, BranchBlock, horarios por membresía, bloqueos del profesional
06bookingReservas multi-servicio, estados, cancelaciones
07discoveryBúsqueda geoespacial, filtros, mapa
08reviewsReseñas portátiles, score ponderado del branch
09notificationsPush baseline + email/WhatsApp/Telegram (fases posteriores)
10billingComisiones, suscripciones, pagos

Transporte de eventos de dominio

Todos los dominios se comunican de forma asíncrona a través de una única tabla domain_events — el outbox universal. Ningún dominio llama directamente a otro en el hot path de escritura; en su lugar escribe un evento en la misma transacción y el bus lo despacha.

Tabla domain_events

sql
CREATE TABLE domain_events (
  id              UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
  event_type      TEXT        NOT NULL,   -- 'booking.confirmed', 'membership.ended', ...
  aggregate_type  TEXT        NOT NULL,   -- 'booking', 'account', ...
  aggregate_id    UUID        NOT NULL,   -- ID del objeto que originó el evento
  payload         JSONB       NOT NULL,   -- campos relevantes (ver catálogo de eventos)
  idempotency_key TEXT        NOT NULL UNIQUE, -- {event_type}:{aggregate_id}:{sequence}
  created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  published_at    TIMESTAMPTZ,            -- NULL = pendiente
  retry_count     INT         NOT NULL DEFAULT 0,
  last_error      TEXT
);

-- Índice parcial para el worker: solo filas pendientes, ordenadas por antigüedad
CREATE INDEX domain_events_pending_idx
  ON domain_events (created_at)
  WHERE published_at IS NULL;

Patrón de escritura (productor)

go
// Dentro de la misma TX que la mutación del dominio:
BEGIN TX
  INSERT INTO bookings (...)
  INSERT INTO domain_events (
    event_type      = 'booking.confirmed',
    aggregate_type  = 'booking',
    aggregate_id    = bookingID,
    payload         = '{"client_id":...,"branch_id":...,"start_at":...}',
    idempotency_key = 'booking.confirmed:' + bookingID
  )
COMMIT
// El evento no existe si la TX hace rollback — garantía transaccional.

Mecanismo de despacho

INSERT en domain_events

Postgres TRIGGER → NOTIFY 'domain_events_channel'

Worker Go (goroutine permanente) LISTEN 'domain_events_channel'

Lee batch de eventos pendientes (published_at IS NULL)

Para cada evento → despacha a handlers registrados en memoria

Si todos los handlers OK → UPDATE published_at = now()
Si algún handler falla  → retry_count++, last_error, backoff exponencial (máx 5)

Fallback (cron cada 30s):
  Recupera eventos con published_at IS NULL AND created_at < now() - 30s
  → reintento para eventos que no recibieron NOTIFY (worker reiniciado, etc.)

Registro de handlers (consumidores)

go
// main.go — al arrancar el proceso
bus.Subscribe("booking.confirmed",  billing.OnBookingConfirmed)
bus.Subscribe("booking.confirmed",  notifications.OnBookingConfirmed)
bus.Subscribe("booking.confirmed",  discovery.OnBookingConfirmed)
bus.Subscribe("booking.completed",  billing.OnBookingCompleted)
bus.Subscribe("booking.completed",  reviews.OnBookingCompleted)
bus.Subscribe("booking.completed",  notifications.OnBookingCompleted)
// ... etc.

Todos los handlers son idempotentes: si el mismo evento llega dos veces (retry), el resultado es el mismo que si llegara una vez.

Catálogo de eventos (MVP)

EventoProductorConsumidores
account.email_verifiedauthaccounts
membership.endedaccountsbooking, catalog, scheduling
branch_block.createdschedulingbooking
branch_block.deletedschedulingbooking
professional_time_off.createdschedulingbooking
professional_time_off.deletedschedulingbooking
availability.invalidatedschedulingdiscovery
branch_service.approvedcatalogdiscovery, notifications
branch_service.rejectedcatalognotifications
branch_service.price_changedcatalogdiscovery
branch_services.bulk_archivedcatalogbooking
booking.hold_createdbooking— (métricas)
booking.hold_expiredbooking— (métricas)
booking.confirmedbookingbilling, notifications, discovery
booking.cancelledbookingbilling, notifications, discovery
booking.completedbookingbilling, reviews, notifications, discovery
booking.no_showbookingbilling, notifications, reviews
review.invitedreviewsnotifications
review.createdreviewsdiscovery, notifications
review.updatedreviewsdiscovery
review.deletedreviewsdiscovery
review.hidden_by_moderationreviewsdiscovery, notifications
review.restoredreviewsdiscovery
review.response_createdreviewsnotifications
review.response_updatedreviewsnotifications
review.response_deletedreviewsnotifications

Cada PRD de dominio especifica el payload exacto de los eventos que produce. La tabla domain_events es la única vía — ningún dominio llama directamente a otro.

Modelo de dominio (15 entidades)

Relaciones resumidas (detalle en los PRDs de dominio):

Account
  ├── 1:1 Professional
  ├── 1:N Business (como dueño)
  ├── 1:N BranchMembership
  └── 1:N Booking (como cliente)

Business
  └── 1:N Branch

Branch
  ├── 1:N BranchSchedule
  ├── 1:N BranchBlock
  ├── 1:N BranchService
  └── 1:N BranchMembership

BranchMembership                         ← unificación de profesional + colaborador
  └── 1:1 ProfessionalMembership (opcional, si type=professional)

ProfessionalMembership
  ├── 1:N ProfessionalBranchSchedule
  └── 1:N BranchService

Professional
  ├── 1:N ProfessionalService
  ├── 1:N ProfessionalMembership
  ├── 1:N ProfessionalBlock
  └── 1:N Review (como target)

Booking
  └── 1:N BookingItem

BookingItem
  └── 0:1 Review

ServiceTemplate   (catálogo global, referencia para copiar)

7. Principios transversales

Estos principios aplican a todos los dominios y a toda decisión de diseño.

Seguridad y privacidad

  • Autorización fail-closed: si el motor de authz no responde, se deniega.
  • Datos personales mínimos: solo lo necesario para operar.
  • Auditoría completa: toda mutación sensible queda registrada (quién, cuándo, qué).
  • Cumplimiento Ley 1581 (Habeas Data Colombia): consentimiento explícito, derecho al olvido, portabilidad.

Escalabilidad

  • Modular monolith primero: extracción a servicios solo cuando una métrica lo exija (no por arquitectura astronauta).
  • Multi-tenancy row-level desde día 1: todas las queries filtran por business_id o account_id implícitamente vía middleware.
  • Geoespacial nativo con PostGIS: preparados para miles de branches por ciudad.

Mantenibilidad

  • Outbox pattern para toda comunicación entre dominios y sincronización con sistemas externos. Dos outboxes: domain_events (eventos de negocio, ver §6) y authz_outbox (relaciones hacia OpenFGA, ver dominio authz).
  • DDD ligero: cada dominio es un paquete Go con fronteras explícitas, sin importar entre dominios salvo vía APIs internas definidas.
  • Tests obligatorios para flujos críticos (reservas, cancelaciones, autorización).
  • Zero shared database schemas entre dominios: cada uno dueño de sus tablas.

Internacionalización

  • Timezones por Branch desde día 1 (una sede en Miami vs Bogotá).
  • Multi-moneda preparada en la data model (DECIMAL + currency ISO), aunque MVP solo soporta COP.
  • i18n de strings en las apps (MVP en español, arquitectura lista para más).

Performance

  • p95 < 300ms para endpoints de lectura críticos.
  • Checks de authz < 20ms p99 (caché en memoria por request).
  • Apps móviles < 2s de tiempo a pantalla interactiva.

Accesibilidad

  • WCAG 2.1 AA como meta en apps y web.
  • Tamaños táctiles mínimos, contrastes, soporte screen readers.

Observabilidad

  • Logging estructurado en JSON desde día 1.
  • Métricas (Prometheus-compatible) expuestas por el backend.
  • Trazas distribuidas (OpenTelemetry) cuando entren servicios externos.

8. Glosario

TérminoDefinición
AccountCuenta única de una persona en la plataforma. Soporta múltiples roles.
BusinessNegocio/empresa. Tiene un dueño (Account) y una o varias Branches.
BranchSede física o virtual (para independientes) del Business. Tiene ubicación y horarios.
ProfessionalPerfil profesional de una persona. Una cuenta tiene 0 o 1 perfiles profesionales.
BranchMembershipRelación de una persona con una Branch. Tipo professional o collaborator.
ProfessionalMembershipSatélite de BranchMembership cuando el tipo es profesional. Carga datos específicos del profesional en esa sede.
CollaboratorPersona que ayuda a gestionar una sede sin prestar servicios. Recepción, coordinación.
ServiceTemplatePlantilla global de servicios (ej. "Corte de cabello"). Base para no partir de cero.
ProfessionalServiceServicio base del profesional, con sus precios como independiente.
BranchServiceCopia de un servicio dentro de una sede, con precios/duración propios. Requiere aprobación.
BookingReserva agrupada de uno o varios servicios en una visita.
BookingItemCada servicio individual dentro de un Booking.
ReviewReseña hecha por un Account al completar un BookingItem. Target: profesional o branch.
ReBACRelationship-Based Access Control. Modelo de autorización basado en relaciones.
OpenFGAMotor open source de ReBAC que usamos para autorización.
Outbox patternPatrón de sincronización: escribe eventos a una tabla dentro de la transacción, un worker los procesa asíncronamente.
Permiso delegableRelación de OpenFGA que un dueño puede otorgar a miembros de su branch (ej. booking_manager).

9. Fuera de alcance del MVP

  • Chat entre usuarios y centros (dominio messaging post-MVP).
  • Pasarelas de pago con procesadores específicos de Colombia (Wompi, ePayco) — integración en fase 5, no en MVP temprano.
  • Integración con Google Reviews — fase 7+.
  • Asistente IA de recepción — fase 7+.
  • Módulo de nómina — fase 7+.
  • Inventario (productos consumibles del centro) — fase 7+.
  • Marketing avanzado (cupones, campañas, email automation) — fase 7+.
  • Aprobaciones multi-firma, políticas temporales, delegación entre colaboradores — no MVP.

10. Índice de PRDs de dominio

#DominioArchivoEstado
01authdomains/01-auth.mdRedactado
02authzdomains/02-authz.mdRedactado
03accountsdomains/03-accounts.mdRedactado
04catalogdomains/04-catalog.mdRedactado
05schedulingdomains/05-scheduling.mdRedactado
06bookingdomains/06-booking.mdRedactado
07discoverydomains/07-discovery.mdRedactado
08reviewsdomains/08-reviews.mdRedactado
09notificationsdomains/09-notifications.mdRedactado
10billingdomains/10-billing.mdRedactado

Este PRD es un documento vivo. Se versiona en el repositorio. Cambios mayores requieren revisión de ambos desarrolladores.

Documentación interna — BeautyHub