<!-- Canonical: https://lucioduran.com/es/blog/cell-based-architecture-blast-radius-isolation
Language: es
Content-SHA256: 7e10a2bbc96ec863a3ece70756a9001e0fa76b9d2e861da51f1eb068b6105765
-->

Architecture

# Arquitectura Basada en Celdas: Aislamiento de Radio de Explosión, Shuffle Sharding y Cómo AWS y Slack Reducen Dominios de Fallo

2025-12-065 min lectura

Lucio Durán
Engineering Manager & AI Solutions Architect

También disponible en: [English](<https://lucioduran.com/blog/cell-based-architecture-blast-radius-isolation>)

## La Idea Central

La arquitectura basada en celdas divide tu sistema en unidades independientes y autónomas llamadas celdas. Cada celda sirve un subconjunto de tenants y contiene toda la infraestructura necesaria para operar independientemente, sus propias bases de datos, caches, colas y compute.

```
 ┌─────────────┐
 │ Router │
 │ (Asignación│
 │ de Celdas) │
 └──────┬──────┘
 │
 ┌────────────┼────────────┐
 │ │ │
 ┌─────▼─────┐ ┌───▼─────┐ ┌───▼─────┐
 │ Celda A │ │ Celda B │ │ Celda C │
 │ ┌───────┐ │ │┌───────┐│ │┌───────┐│
 │ │API │ │ ││API ││ ││API ││
 │ │DB │ │ ││DB ││ ││DB ││
 │ │Cache │ │ ││Cache ││ ││Cache ││
 │ │Cola │ │ ││Cola ││ ││Cola ││
 │ └───────┘ │ │└───────┘│ │└───────┘│
 └───────────┘ └─────────┘ └─────────┘
```

La propiedad crítica: las celdas no comparten nada. Si la base de datos de la Celda A se cae, las celdas B y C siguen operando.

## Enrutamiento de Celdas

```javascript
const CELL_ENDPOINTS = {
 0: 'https://cell-a.internal.example.com',
 1: 'https://cell-b.internal.example.com',
 2: 'https://cell-c.internal.example.com',
 3: 'https://cell-d.internal.example.com',
};

function assignCell(tenantId) {
 const hash = fnv1a(tenantId);
 return hash % Object.keys(CELL_ENDPOINTS).length;
}

export default {
 async fetch(request) {
 const tenantId = extractTenantId(request);
 const cellIndex = assignCell(tenantId);
 const cellUrl = CELL_ENDPOINTS[cellIndex];

 const cellRequest = new Request(
 `${cellUrl}${new URL(request.url).pathname}`,
 request,
 );

 try {
 const response = await fetch(cellRequest, { cf: { timeout: 10000 } });
 response.headers.set('X-Cell', `cell-${cellIndex}`);
 return response;
 } catch (error) {
 return new Response('Servicio temporalmente no disponible', { status: 503 });
 }
 },
};
```

## Shuffle Sharding: Reduciendo el Radio de Explosión

```python
import hashlib
import random

def shuffle_shard(tenant_id, num_cells, shard_size=2):
 seed = int(hashlib.sha256(tenant_id.encode()).hexdigest(), 16)
 rng = random.Random(seed)
 cells = list(range(num_cells))
 rng.shuffle(cells)
 return cells[:shard_size]

# Con 8 celdas y shards de 2 celdas:
# - Fallo de una celda afecta: 25% de tenants como máximo
# - Ese es tenants hacen failover a su celda secundaria
# - Dos fallos simultáneos afectan: ~3.6% de tenants
```

[AWS](<https://aws.amazon.com/>) Route 53 usa este patrón. Cada zona DNS se asigna a 4 de sus 2.048 shards virtuales. La probabilidad de que un "vecino ruidoso" (un DDoS en una zona) afecte a otra zona específica es insignificantemente pequeña.

## Infraestructura de Celdas con Terraform

```hcl
variable "cell_id" { type = string }
variable "cell_index" { type = number }

resource "aws_vpc" "cell" {
 cidr_block = "10.${var.cell_index}.0.0/16"
 tags = { Name = "cell-${var.cell_id}" }
}

resource "aws_rds_cluster" "cell_db" {
 cluster_identifier = "cell-${var.cell_id}-db"
 engine = "aurora-postgresql"
 engine_version = "16.1"
 tags = { Cell = var.cell_id }
}

resource "aws_elasticache_replication_group" "cell_cache" {
 replication_group_id = "cell-${var.cell_id}-redis"
 node_type = "cache.r7g.large"
 num_cache_clusters = 2
 tags = { Cell = var.cell_id }
}
```

Los VPCs están aislados, las celdas no pueden comunicarse accidentalmente entre sí. Esto es deliberado.

## Estrategia de Deployment: Celdas Canary

```yaml
stages:
 - name: canary-cell
 cells: ["cell-a"]
 validation:
 - health_check: 5m
 - error_rate: < 0.1%
 - latency_p99: < 200ms
 rollback_on_failure: true

 - name: second-wave
 cells: ["cell-b"]
 depends_on: canary-cell
 delay: 15m

 - name: remaining
 cells: ["cell-c", "cell-d"]
 depends_on: second-wave
 delay: 30m
```

Así es exactamente como un incidente de migración de base de datos funciona con celdas: la migración corre en Celda A, se rompe, se hace rollback de Celda A, y las celdas B/C/D nunca ven la migración mala. En vez de 45 minutos de downtime total, son 5 minutos de outage parcial para un cuarto de los usuarios.

## Monitoreo Por Celda

```yaml
groups:
 - name: cell_metrics
 rules:
 - record: cell:request_rate:5m
 expr: sum(rate(http_requests_total{job="api"}[5m])) by (cell)
 - record: cell:error_rate:5m
 expr: |
 sum(rate(http_requests_total{status=~"5.."}[5m])) by (cell)
 / sum(rate(http_requests_total[5m])) by (cell)
 - name: cell_alerts
 rules:
 - alert: CellHighErrorRate
 expr: cell:error_rate:5m > 0.01
 for: 5m
 labels:
 severity: critical
```

La label ` cell ` en cada métrica no es negociable. Sin ella, no es posible distinguir si un spike de error rate es un problema global o de una sola celda.

## Trade-offs y Costos Operacionales

**Costo**: Correr N copias de todo significa que 4 celdas es aproximadamente 4x el costo de base de datos. En la práctica, las celdas agregan alrededor de 40% a los costos de infraestructura.

**Complejidad operacional**: Cada procedimiento operacional necesita un paso de "¿qué celda?". Migraciones de base de datos, cambios de schema, actualizaciones de config, todo necesita ser cell-aware.

**Migración de datos**: Mover un tenant entre celdas es la operación más difícil. Requiere una pausa de escritura coordinada, copia de datos y actualización de routing.

**Features globales**: Algunas cosas inherentemente cruzan celdas, búsqueda global, analytics cross-tenant. Necesitan una capa de agregación separada que lee de todas las celdas.

## Para Cerrar

La arquitectura basada en celdas es la opción nuclear para aislamiento de radio de explosión. Funciona, los sistemas pasan de "un mal deployment tumba todo" a "un mal deployment afecta 25% de usuarios por 5 minutos", pero el costo operacional es real.

Se recomienda comenzar de forma simple: circuit breakers, bulkheads, deployments multi-AZ. Conviene avanzar hacia celdas cuando un dominio de fallo único que afecta a todos los usuarios se convierte en un riesgo existencial para el negocio. Al implementar celdas, es recomendable comenzar con 4, mantenerlas verdaderamente independientes, e invertir fuertemente en observabilidad por celda.

La arquitectura es la parte fácil. Convivir con ella es la parte difícil.

## Preguntas frecuentes

### ¿Cómo difiere la arquitectura basada en celdas de los microservicios?

Los microservicios dividen por función, un servicio para auth, otro para pagos. La arquitectura basada en celdas divide por tenant. Cada celda corre TODOS los servicios para un SUBCONJUNTO de tenants. La diferencia clave es el boundary de fallo: un fallo de microservicio afecta a todos los usuarios de esa función, mientras un fallo de celda afecta solo a usuarios asignados a esa celda.

### ¿Qué es shuffle sharding y por qué importa?

Shuffle sharding asigna cada tenant a un subconjunto random de celdas (típicamente 2 de N). Si la celda A falla, solo los tenants cuyo shard set incluye la celda A son afectados, y ese es tenants hacen failover a su segunda celda. Con 8 celdas y shards de 2 celdas, cualquier fallo de una celda afecta solo al 25% de tenants, y la mayoría se recupera automáticamente.

### ¿Cuándo la arquitectura basada en celdas es overkill?

Si tu sistema tiene menos de 1000 tenants, sirve menos de 10K requests/segundo, o tiene un equipo menor a 5 ingenieros. El overhead operacional de mantener múltiples stacks de infraestructura independientes es significativo. Considerá aislamiento de fallos más simple primero, multi-región active-active, o simplemente circuit breaking exhaustivo.

## Tips

1. **Tip:** Se recomienda comenzar con 4 celdas, no 40. Cada celda es una copia completa de tu infraestructura, networking, bases de datos, caches, colas. Más celdas significa mejor aislamiento pero exponencialmente más overhead operacional. Empezar con 8 y volver a 4 es común una vez que la carga de monitoreo se vuelve insostenible para equipos pequeños.
2. **Tip:** La asignación de celdas debe ser determinista y consistente, es necesario aplicar hash al tenant ID, no al request. Si el mismo usuario accede a diferentes celdas en diferentes requests, se pierde localidad de datos y eficiencia de cache. Se recomienda utilizar ` cell = tenantId % numCells ` o consistent hashing.
3. **Tip:** Siempre mantener una celda como 'celda canary' que recibe deployments primero. Si el deployment rompe la canary, contuviste el radio de explosión a ~25% de usuarios (con 4 celdas) en vez de 100%. Este enfoque puede prevenir que una mala migración corrompa registros de pagos en todo el sistema.
4. **Tip:** No compartas bases de datos entre celdas. Bases de datos compartidas son dominios de fallo compartidos. Si absolutamente se necesita compartir estado (como un directorio global de usuarios), configurarlo como read-only desde las celdas y write-only desde un control plane separado.

- cell-based-architecture
- blast-radius
- shuffle-sharding
- aislamiento-de-fallos
- sistemas-distribuidos
- aws
- slack
- resiliencia
- alta-disponibilidad
- arquitectura

[Volver al Blog](<https://lucioduran.com/es/blog>)
