Nuevas Épicas (MCH-029 a MCH-033): - Infraestructura SaaS multi-tenant - Auth Social (OAuth2) - Auditoría Empresarial - Feature Flags - Onboarding Wizard Nuevas Integraciones (INT-010 a INT-014): - Email Providers (SendGrid, Mailgun, SES) - Storage Cloud (S3, GCS, Azure) - OAuth Social - Redis Cache - Webhooks Outbound Nuevos ADRs (0004 a 0011): - Notifications Realtime - Feature Flags Strategy - Storage Abstraction - Webhook Retry Strategy - Audit Log Retention - Rate Limiting - OAuth Social Implementation - Email Multi-provider Actualizados: - MASTER_INVENTORY.yml - CONTEXT-MAP.yml - HERENCIA-SIMCO.md - Mapas de documentación Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
228 lines
5.1 KiB
Markdown
228 lines
5.1 KiB
Markdown
---
|
|
id: ADR-0005
|
|
type: ADR
|
|
title: "Feature Flags por Plan"
|
|
status: Accepted
|
|
decision_date: 2026-01-10
|
|
updated_at: 2026-01-10
|
|
simco_version: "4.0.1"
|
|
stakeholders:
|
|
- "Equipo MiChangarrito"
|
|
tags:
|
|
- feature-flags
|
|
- plans
|
|
- multi-tenant
|
|
---
|
|
|
|
# ADR-0005: Feature Flags por Plan
|
|
|
|
## Metadata
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **ID** | ADR-0005 |
|
|
| **Estado** | Accepted |
|
|
| **Fecha** | 2026-01-10 |
|
|
| **Autor** | Architecture Team |
|
|
| **Supersede** | - |
|
|
|
|
---
|
|
|
|
## Contexto
|
|
|
|
MiChangarrito ofrece multiples planes de suscripcion (Basic, Pro, Enterprise) con diferentes funcionalidades. Necesitamos un sistema que:
|
|
|
|
1. Habilite/deshabilite features segun el plan
|
|
2. Permita overrides para beta testing
|
|
3. Sea rapido en evaluacion (no impacte rendimiento)
|
|
4. Funcione tanto en backend como frontend
|
|
|
|
---
|
|
|
|
## Decision
|
|
|
|
**Implementamos un sistema de feature flags propio con cache en Redis y evaluacion en tiempo real.**
|
|
|
|
Los flags se definen globalmente, se asocian a planes, y pueden tener overrides por tenant o usuario. La evaluacion usa cache Redis para latencia <10ms.
|
|
|
|
```typescript
|
|
// Evaluacion de flag
|
|
const hasFeature = await featureFlagService.evaluate('ai_assistant', {
|
|
tenantId,
|
|
userId,
|
|
planId,
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Alternativas Consideradas
|
|
|
|
### Opcion 1: LaunchDarkly/Flagsmith (SaaS)
|
|
- **Pros:**
|
|
- Funcionalidad completa
|
|
- Dashboard listo
|
|
- SDKs para todo
|
|
- **Cons:**
|
|
- Costo adicional
|
|
- Dependencia externa
|
|
- Datos salen del sistema
|
|
|
|
### Opcion 2: Unleash (Open Source)
|
|
- **Pros:**
|
|
- Open source
|
|
- Self-hosted
|
|
- Feature completo
|
|
- **Cons:**
|
|
- Infraestructura adicional
|
|
- Complejidad de mantener
|
|
- Overhead para nuestro caso
|
|
|
|
### Opcion 3: Implementacion propia (Elegida)
|
|
- **Pros:**
|
|
- Sin dependencias externas
|
|
- Integrado con nuestro sistema de planes
|
|
- Cache en Redis existente
|
|
- Control total
|
|
- **Cons:**
|
|
- Desarrollo inicial
|
|
- Sin UI avanzada (la construimos)
|
|
- Mantenimiento propio
|
|
|
|
---
|
|
|
|
## Consecuencias
|
|
|
|
### Positivas
|
|
|
|
1. **Integracion:** Se integra directamente con plans y tenants
|
|
2. **Performance:** Cache Redis garantiza <10ms
|
|
3. **Control:** Logica de negocio en nuestro codigo
|
|
4. **Costo:** Sin gastos adicionales
|
|
|
|
### Negativas
|
|
|
|
1. **Desarrollo:** Tiempo de implementacion
|
|
2. **Features:** Menos features que soluciones SaaS
|
|
3. **Mantenimiento:** Responsabilidad propia
|
|
|
|
---
|
|
|
|
## Implementacion
|
|
|
|
### Modelo de Datos
|
|
|
|
```sql
|
|
-- Flags globales
|
|
CREATE TABLE feature_flags.flags (
|
|
id UUID PRIMARY KEY,
|
|
key VARCHAR(100) UNIQUE NOT NULL,
|
|
description TEXT,
|
|
default_value BOOLEAN DEFAULT false
|
|
);
|
|
|
|
-- Flags por plan
|
|
CREATE TABLE feature_flags.plan_flags (
|
|
flag_id UUID REFERENCES flags(id),
|
|
plan_id UUID REFERENCES billing.plans(id),
|
|
value BOOLEAN NOT NULL,
|
|
PRIMARY KEY (flag_id, plan_id)
|
|
);
|
|
|
|
-- Overrides
|
|
CREATE TABLE feature_flags.flag_overrides (
|
|
flag_id UUID REFERENCES flags(id),
|
|
tenant_id UUID,
|
|
user_id UUID,
|
|
value BOOLEAN NOT NULL,
|
|
expires_at TIMESTAMP
|
|
);
|
|
```
|
|
|
|
### Evaluador
|
|
|
|
```typescript
|
|
async evaluate(key: string, context: FlagContext): Promise<boolean> {
|
|
// 1. Buscar en cache
|
|
const cacheKey = `flags:${key}:${context.tenantId}:${context.userId}`;
|
|
const cached = await this.redis.get(cacheKey);
|
|
if (cached !== null) return cached === 'true';
|
|
|
|
// 2. Buscar override de usuario
|
|
const userOverride = await this.findOverride(key, { userId: context.userId });
|
|
if (userOverride) {
|
|
await this.cache(cacheKey, userOverride.value);
|
|
return userOverride.value;
|
|
}
|
|
|
|
// 3. Buscar override de tenant
|
|
const tenantOverride = await this.findOverride(key, { tenantId: context.tenantId });
|
|
if (tenantOverride) {
|
|
await this.cache(cacheKey, tenantOverride.value);
|
|
return tenantOverride.value;
|
|
}
|
|
|
|
// 4. Evaluar por plan
|
|
const planValue = await this.getPlanValue(key, context.planId);
|
|
if (planValue !== null) {
|
|
await this.cache(cacheKey, planValue);
|
|
return planValue;
|
|
}
|
|
|
|
// 5. Valor por defecto
|
|
const flag = await this.getFlag(key);
|
|
await this.cache(cacheKey, flag.default_value);
|
|
return flag.default_value;
|
|
}
|
|
```
|
|
|
|
### Uso en Backend
|
|
|
|
```typescript
|
|
@UseGuards(FeatureFlagGuard)
|
|
@FeatureFlag('advanced_reports')
|
|
@Get('reports/advanced')
|
|
getAdvancedReports() {
|
|
// Solo accesible si el flag esta habilitado
|
|
}
|
|
```
|
|
|
|
### Uso en Frontend
|
|
|
|
```tsx
|
|
function Dashboard() {
|
|
const canUseAI = useFeatureFlag('ai_assistant');
|
|
|
|
return (
|
|
<div>
|
|
{canUseAI && <AIAssistant />}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Flags Iniciales
|
|
|
|
| Key | Descripcion | Basic | Pro | Enterprise |
|
|
|-----|-------------|-------|-----|------------|
|
|
| ai_assistant | Asistente IA | false | true | true |
|
|
| advanced_reports | Reportes avanzados | false | true | true |
|
|
| api_access | API publica | false | false | true |
|
|
| white_label | Sin branding | false | false | true |
|
|
| multi_location | Multiples ubicaciones | false | true | true |
|
|
| custom_integrations | Integraciones custom | false | false | true |
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [Feature Flags Best Practices](https://martinfowler.com/articles/feature-toggles.html)
|
|
- [MCH-032: Feature Flags](../01-epicas/MCH-032-feature-flags.md)
|
|
|
|
---
|
|
|
|
**Fecha decision:** 2026-01-10
|
|
**Autores:** Architecture Team
|