CommunityProgramación y desarrollogithub.com

SaaS - Guía de Arquitectura SaaS

Habilidad de agente / proyecto relacionado con lanzamiento o video de demostración de producto SaaS: Satcomx00-x00/skills-db

¿Qué es SaaS - Guía de Arquitectura SaaS?

Esta habilidad proporciona una guía completa y práctica para construir productos SaaS modernos y listos para producción, cubriendo aspectos clave como multi-tenencia, facturación basada en uso, crecimiento impulsado por producto (PLG), integración de funciones de IA, observabilidad, cumplimiento normativo y seguridad. Está diseñada para desarrolladores y equipos que utilizan asistentes de IA como Claude Code, Cursor o Codex, y les ofrece un punto de partida sólido con patrones de arquitectura probados, fragmentos de código (como repositorios con ámbito de tenant y endpoints de streaming con IA), y referencias a herramientas y servicios esenciales (Stripe, PostgreSQL RLS, OpenTelemetry, pgvector). La habilidad se estructura en secciones detalladas que van desde la elección del modelo de aislamiento de datos hasta la implementación de gateros de funciones y alertas a nivel de tenant. Al adoptar estos patrones, los usuarios pueden acelerar el desarrollo, evitar errores comunes y construir aplicaciones SaaS escalables, seguras y centradas en el cliente.

Compatible con~Claude Code✓Codex CLI~Cursor
npx skills add Satcomx00-x00/skills-db

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

SaaS

Estado del arte en SaaS — multi-tenencia, facturación basada en uso, PLG, integración de funciones de IA, observabilidad moderna, cumplimiento normativo y seguridad para productos SaaS listos para producción.

Instrucciones

Multi-Tenencia

Elige el modelo de aislamiento adecuado para tu caso de uso:

ModeloDescripciónCuándo usarlo
Esquema compartidoTodos los tenants comparten tablas; las filas se etiquetan con tenant_idEtapa temprana, sensible al costo, cumplimiento sencillo
Esquema por tenantEsquema de BD separado por tenantAislamiento medio, migraciones fáciles por tenant
BD por tenantBase de datos separada por tenantEmpresarial, residencia de datos estricta, alto cumplimiento (SOC 2, HIPAA)
HíbridoDatos centrales compartidos; datos sensibles aisladosIndustrias reguladas con ofertas de niveles mixtos

Reglas para tenencia de esquema compartido:

  • Cada consulta debe filtrar por tenant_id — imponerlo mediante un repositorio base o middleware ORM, no mediante la disciplina del desarrollador
  • Usa la Seguridad a Nivel de Fila (RLS) de PostgreSQL como segunda capa de defensa:
    ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
    CREATE POLICY tenant_isolation ON orders
      USING (tenant_id = current_setting('app.current_tenant')::uuid);
    
  • Indexa tenant_id en cada tabla con ámbito de tenant
  • Escribe pruebas de integración que verifiquen el aislamiento de datos entre tenants
  • Define rutas en el almacenamiento de objetos: s3://bucket/{tenantId}/uploads/{fileId}
  • Nunca expongas IDs secuenciales predecibles — usa UUIDs (v7 para ordenabilidad)

Suscripción y Facturación

Suscripciones de tarifa plana

  • Delega la facturación a un proveedor (Stripe, Paddle, LemonSqueezy) — nunca implementes el procesamiento de pagos internamente
  • Almacena solo customerId y subscriptionId en tu BD; nunca datos de tarjeta
  • Sincroniza el estado mediante webhooks — trata tu BD como una réplica de solo lectura eventualmente consistente de la verdad del proveedor de facturación:
    customer.subscription.created  → activar plan
    customer.subscription.updated  → actualizar / degradar / cambiar asientos
    customer.subscription.deleted  → cancelar + iniciar período de gracia
    invoice.payment_failed         → proceso de cobro: notificar → restringir → cancelar
    
  • Usa el id del evento webhook como clave de idempotencia — almacénalo antes de procesar; omítelo si ya se ha visto
  • Almacena los payloads de webhook sin procesar para auditoría y reproducción

Facturación basada en uso / por consumo (predeterminado moderno)

  • Rastrea eventos de uso a medida que ocurren (llamadas API, asientos, tokens consumidos, GB de almacenamiento):
    await stripe.subscriptionItems.createUsageRecord(subscriptionItemId, {
      quantity: tokensUsed,
      timestamp: Math.floor(Date.now() / 1000),
      action: 'increment',
    });
    
  • Proporciona un panel de uso en vivo a los clientes — las sorpresas en el día de facturación causan abandono
  • Implementa límites suaves (advertir al 80%) y límites duros (bloquear al 100%) por período de facturación
  • Considera un nivel gratuito + actualización basada en uso como rampa de entrada para el Crecimiento Impulsado por Producto (PLG)

Crecimiento Impulsado por Producto (PLG)

  • Nivel gratuito / freemium — permite a los usuarios experimentar el valor antes de pedir pago; establece límites que hagan que los planes de pago sean obviamente valiosos
  • Tiempo hasta el valor (TTV) — mide y minimiza el tiempo desde el registro hasta la primera acción significativa ("momento ajá")
  • Indicaciones de actualización en la aplicación — muestra llamadas a la acción de actualización en el momento contextual en que se alcanza un límite del plan, no en una página de configuración genérica
  • Clientes potenciales calificados por producto (PQL) — puntúa a los usuarios por señales de uso del producto (no solo solicitudes de demostración) para derivar a ventas
  • Funciones virales / colaborativas — invitar a compañeros de equipo, compartir informes, páginas públicas — cada acción es un canal de distribución

Integración de Funciones de IA

Se espera que los productos SaaS modernos incluyan capacidades nativas de IA. Aplica estos patrones:

Asistentes y copilotos de IA:

  • Usa respuestas en streaming (SSE / ReadableStream) para que los usuarios vean los tokens a medida que llegan — nunca los hagas esperar por una respuesta completa
  • Siempre haz que el resultado de la IA sea editable — los usuarios necesitan corregir, refinar y poseer el resultado
  • Muestra señales de confianza y cita fuentes (RAG) para generar confianza

Generación Aumentada por Recuperación (RAG):

  • Almacena embeddings vectoriales por tenant en un espacio de nombres aislado (Pinecone, pgvector, Qdrant) — nunca mezcles datos de tenants
  • Incrusta contenido en la escritura; consulta en la lectura con similitud de coseno
  • Fragmenta documentos de manera inteligente (512–1024 tokens con superposición); re-incrusta cuando el contenido de origen cambie

Controles de uso y costos:

  • Rastrea el consumo de tokens LLM por tenant — factura o limita la velocidad en consecuencia
  • Almacena en caché prompts deterministas (misma entrada → misma salida) para reducir costos
  • Usa modelos más baratos para clasificación/triaje; reserva modelos frontera para tareas de generación

Privacidad de datos:

  • Ofrece un anexo de tratamiento de datos (DPA) que indique explícitamente si los datos del tenant se utilizan para el entrenamiento del modelo
  • Proporciona una opción de exclusión; los clientes empresariales la exigirán

Feature Flags y Control de Acceso por Plan

  • Controla el acceso a funciones en la capa de servicio — la ocultación en el cliente es un adorno de UI, no seguridad
  • Centraliza la lógica de elegibilidad en un helper único can(tenant, feature):
    function can(tenant: Tenant, feature: Feature): boolean {
      return tenant.plan.features.includes(feature);
    }
    
  • Separa el despliegue de funciones de su lanzamiento usando un sistema de flags (LaunchDarkly, Unleash, GrowthBook o una tabla simple respaldada por BD)
  • Usa flags para: despliegues graduales (% de tenants), programas beta, pruebas A/B, interruptores de apagado

Onboarding

  • Minimiza el TTV: guía a los usuarios a su primera acción significativa en < 5 minutos
  • Usa un flujo de onboarding basado en checklist con indicadores de progreso claros
  • Precarga datos de muestra / demo para que el producto no se sienta vacío en el primer uso
  • Recopila la cantidad mínima de datos por adelantado; difiere todo lo demás
  • Dispara correos electrónicos de onboarding basados en eventos del producto (no solo en el tiempo): bienvenida al registrarse, recordatorio si no han completado el paso 2 después de 24h, celebración del primer hito
  • Ofrece un recorrido interactivo del producto (no un video) para que los usuarios aprendan haciendo

Observabilidad y Operaciones

Registro estructurado — cada línea de log debe incluir:

{ "level": "info", "msg": "...", "tenantId": "...", "userId": "...", "requestId": "...", "timestamp": "..." }

Trazado distribuido con OpenTelemetry:

  • Instrumentaliza a nivel del framework (Next.js instrumentation.ts, middleware Express)
  • Propaga el contexto de traza a través de los límites del servicio (cabecera traceparent)
  • Exporta a tu backend de elección (Jaeger, Honeycomb, Datadog, Grafana Tempo)

Métricas por tenant a seguir:

  • Usuarios activos (DAU/MAU), volumen de llamadas API, tasa de error, latencia p50/p95/p99, adopción de funciones, almacenamiento consumido

Alertas:

  • Alerta sobre anomalías a nivel de tenant — pico repentino de errores, caída de uso, fallos de facturación
  • Define SLO por nivel: gratuito (mejor esfuerzo), pro (99.5%), empresarial (99.9%)

Herramientas de administración (internas):

  • Suplantar a un tenant para soporte (auditar cada suplantación)
  • Ver registro de auditoría, gestionar suscripciones, anular feature flags, disparar re-onboarding
  • Construir un panel de salud: estado por tenant, errores recientes, tendencias de uso

Datos, Analítica y Cumplimiento Normativo

Seguimiento de eventos:

  • Emite eventos de producto estructurados (user.signed_up, feature.used, subscription.upgraded) a un data warehouse (Snowflake, BigQuery) o CDP (Segment, PostHog, RudderStack)
  • Usa estos eventos para la predicción de abandono, análisis de cohortes y puntuación PLG

GDPR / privacidad:

  • Proporciona un endpoint de exportación de datos y un endpoint de eliminación de cuenta — requerido por GDPR y esperado por compradores empresariales
  • Documenta tu política de retención de datos; implementa purgas automatizadas
  • Usa gestión de consentimiento para cookies de marketing y analítica

SOC 2 / higiene de cumplimiento (preparación empresarial):

  • Mantén un registro de auditoría de todas las acciones de usuario que cambian el estado (quién, qué, cuándo, desde qué IP)
  • Implementa control de acceso basado en roles (RBAC) con privilegios mínimos por defecto
  • Exige MFA para todas las cuentas de administración; ofrece SSO (SAML/OIDC) para tenants empresariales
  • Realiza escaneos regulares de vulnerabilidades de dependencias y pruebas de penetración
  • Mantén una política de divulgación de vulnerabilidades

Aislamiento de Datos y Seguridad

  • Valida tenant_id en cada llamada API contra la sesión autenticada — nunca confíes en el cuerpo de la solicitud
  • Limita la velocidad por tenant para evitar efectos de vecino ruidoso
  • Hashea las claves API en la BD (bcrypt o Argon2); nunca almacenes texto plano
  • Exige HTTPS; establece Strict-Transport-Security con un max-age largo
  • Usa HttpOnly; Secure; SameSite=Lax para las cookies de sesión

Ejemplos

// Repositorio con ámbito de tenant y contexto RLS
class TenantRepository<T> {
  constructor(
    private readonly tenantId: string,
    private readonly db: PrismaClient,
  ) {}

  protected async withTenantContext<R>(fn: () => Promise<R>): Promise<R> {
    return this.db.$transaction(async (tx) => {
      await tx.$executeRaw`SELECT set_config('app.current_tenant', ${this.tenantId}, true)`;
      return fn();
    });
  }

  async findById(id: string): Promise<T | null> {
    return this.withTenantContext(() =>
      (this.db as any).findFirst({ where: { id, tenantId: this.tenantId } })
    );
  }
}

// Endpoint de streaming con IA y medición de uso por tenant
export async function POST(req: Request) {
  const session = await getSession(req);
  const { prompt } = await req.json();

  const stream = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'user', content: prompt }],
    stream: true,
  });

  let tokensUsed = 0;
  const readable = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        const text = chunk.choices[0]?.delta?.content ?? '';
        controller.enqueue(new TextEncoder().encode(text));
        tokensUsed += chunk.usage?.completion_tokens ?? 0;
      }
      controller.close();
      // medir uso después de que el streaming se complete
      await meteringService.record(session.tenantId, 'ai_tokens', tokensUsed);
    },
  });

  return new Response(readable, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
}

// Webhook de Stripe con idempotencia
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
  const event = stripe.webhooks.constructEvent(
    req.body, req.headers['stripe-signature']!, process.env.STRIPE_WEBHOOK_SECRET!
  );

  const seen = await db.webhookEvent.findUnique({ where: { stripeEventId: event.id } });
  if (seen) return res.sendStatus(200);
  await db.webhookEvent.create({ data: { stripeEventId: event.id, payload: event } });

  switch (event.type) {
    case 'customer.subscription.updated':
      await subscriptionService.sync(event.data.object);
      break;
    case 'customer.subscription.deleted':
      await subscriptionService.cancel(event.data.object.metadata.tenantId);
      break;
  }

  res.sendStatus(200);
});

Referencias

Skills relacionados