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
- 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.
- 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.
- 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.
- 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:
| Fuente | Momento | Detalle |
|---|---|---|
| Comisión por reserva | Desde día 1 | Porcentaje por cada reserva completada. Aplica a centros y a profesionales independientes. |
| Suscripción premium | Fase 6+ | Nómina, asistente IA, integración Google Reviews, marketing avanzado, WhatsApp Business. |
| Servicios adicionales | Futuro | Campañ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".
| Fase | Foco | Alcance resumen | Entregable para usuario |
|---|---|---|---|
| 0. Base | Identidad y autorización | auth + authz + infraestructura VPS + CI/CD + apps scaffolded | Usuarios se registran y entran |
| 1. Core | Catálogo y agenda | accounts + catalog + scheduling | Un negocio puede crear su perfil, servicios y horarios |
| 2. Reserva | Flujo completo de booking | booking | Un usuario puede reservar un servicio |
| 3. Discovery | Mapa y búsqueda | discovery | Usuario descubre sin saber a quién buscar |
| 4. Reputación | Reseñas portátiles | reviews | Profesionales construyen trayectoria |
| 5. Pagos y comisión | Monetización base | billing (MVP) | Plataforma empieza a generar ingresos |
| 6. Notificaciones | Retención y UX | notifications (push → email → WhatsApp) | Menos no-shows, más engagement |
| 7. Premium | Features avanzados | Extensiones 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
authyauthzestén listos.
6. Arquitectura de alto nivel
Stack tecnológico
| Capa | Tecnología | Razón |
|---|---|---|
| Backend | Go (modular monolith) | Eficiencia memoria+costo, excelente para APIs, sin overhead microservicios en MVP |
| Autorización | OpenFGA (ReBAC) | CNCF, inspirado en Zanzibar, evita deuda técnica de RBAC custom |
| Base de datos | PostgreSQL + PostGIS | Geoespacial nativo para mapa, madurez, multi-tenancy row-level |
| Caché/colas | Redis | Sesiones, caché de checks de autorización, cola de outbox |
| Consumer App | React Native (Expo) | OTA updates, iteración rápida, reutilización de talento JS |
| Business App | React Native (Expo) | Misma razón, equipo pequeño puede mantener ambas |
| Web Admin | React | Ecosistema maduro, compatible con código de RN |
| Infraestructura | VPS con Docker Compose | Bajo 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)
│
RedisResponsabilidad por dominio
| # | Dominio | Responsabilidad |
|---|---|---|
| 01 | auth | Identidad: registro, login, sesiones, OTP, recuperación |
| 02 | authz | Autorización: schema ReBAC, Check, Write, sync outbox |
| 03 | accounts | Account, Business, Branch, Professional, BranchMembership |
| 04 | catalog | ServiceTemplate, ProfessionalService, BranchService, flujo de aprobación |
| 05 | scheduling | BranchSchedule, BranchBlock, horarios por membresía, bloqueos del profesional |
| 06 | booking | Reservas multi-servicio, estados, cancelaciones |
| 07 | discovery | Búsqueda geoespacial, filtros, mapa |
| 08 | reviews | Reseñas portátiles, score ponderado del branch |
| 09 | notifications | Push baseline + email/WhatsApp/Telegram (fases posteriores) |
| 10 | billing | Comisiones, 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
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)
// 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)
// 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)
| Evento | Productor | Consumidores |
|---|---|---|
account.email_verified | auth | accounts |
membership.ended | accounts | booking, catalog, scheduling |
branch_block.created | scheduling | booking |
branch_block.deleted | scheduling | booking |
professional_time_off.created | scheduling | booking |
professional_time_off.deleted | scheduling | booking |
availability.invalidated | scheduling | discovery |
branch_service.approved | catalog | discovery, notifications |
branch_service.rejected | catalog | notifications |
branch_service.price_changed | catalog | discovery |
branch_services.bulk_archived | catalog | booking |
booking.hold_created | booking | — (métricas) |
booking.hold_expired | booking | — (métricas) |
booking.confirmed | booking | billing, notifications, discovery |
booking.cancelled | booking | billing, notifications, discovery |
booking.completed | booking | billing, reviews, notifications, discovery |
booking.no_show | booking | billing, notifications, reviews |
review.invited | reviews | notifications |
review.created | reviews | discovery, notifications |
review.updated | reviews | discovery |
review.deleted | reviews | discovery |
review.hidden_by_moderation | reviews | discovery, notifications |
review.restored | reviews | discovery |
review.response_created | reviews | notifications |
review.response_updated | reviews | notifications |
review.response_deleted | reviews | notifications |
Cada PRD de dominio especifica el
payloadexacto de los eventos que produce. La tabladomain_eventses 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_idoaccount_idimplí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) yauthz_outbox(relaciones hacia OpenFGA, ver dominioauthz). - 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érmino | Definición |
|---|---|
| Account | Cuenta única de una persona en la plataforma. Soporta múltiples roles. |
| Business | Negocio/empresa. Tiene un dueño (Account) y una o varias Branches. |
| Branch | Sede física o virtual (para independientes) del Business. Tiene ubicación y horarios. |
| Professional | Perfil profesional de una persona. Una cuenta tiene 0 o 1 perfiles profesionales. |
| BranchMembership | Relación de una persona con una Branch. Tipo professional o collaborator. |
| ProfessionalMembership | Satélite de BranchMembership cuando el tipo es profesional. Carga datos específicos del profesional en esa sede. |
| Collaborator | Persona que ayuda a gestionar una sede sin prestar servicios. Recepción, coordinación. |
| ServiceTemplate | Plantilla global de servicios (ej. "Corte de cabello"). Base para no partir de cero. |
| ProfessionalService | Servicio base del profesional, con sus precios como independiente. |
| BranchService | Copia de un servicio dentro de una sede, con precios/duración propios. Requiere aprobación. |
| Booking | Reserva agrupada de uno o varios servicios en una visita. |
| BookingItem | Cada servicio individual dentro de un Booking. |
| Review | Reseña hecha por un Account al completar un BookingItem. Target: profesional o branch. |
| ReBAC | Relationship-Based Access Control. Modelo de autorización basado en relaciones. |
| OpenFGA | Motor open source de ReBAC que usamos para autorización. |
| Outbox pattern | Patrón de sincronización: escribe eventos a una tabla dentro de la transacción, un worker los procesa asíncronamente. |
| Permiso delegable | Relació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
messagingpost-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
| # | Dominio | Archivo | Estado |
|---|---|---|---|
| 01 | auth | domains/01-auth.md | Redactado |
| 02 | authz | domains/02-authz.md | Redactado |
| 03 | accounts | domains/03-accounts.md | Redactado |
| 04 | catalog | domains/04-catalog.md | Redactado |
| 05 | scheduling | domains/05-scheduling.md | Redactado |
| 06 | booking | domains/06-booking.md | Redactado |
| 07 | discovery | domains/07-discovery.md | Redactado |
| 08 | reviews | domains/08-reviews.md | Redactado |
| 09 | notifications | domains/09-notifications.md | Redactado |
| 10 | billing | domains/10-billing.md | Redactado |
Este PRD es un documento vivo. Se versiona en el repositorio. Cambios mayores requieren revisión de ambos desarrolladores.