Zeebe Design Philosophy
En 2017, Camunda decidió que Camunda 7 (basado en DB tradicional) no podía escalar a la era de microservicios. Daniel Meyer pasó ~2 años diseñando Zeebe desde cero, con la decisión arquitectónica decisiva de usar stream processing internamente en vez de una database. Esa elección sola es lo que diferencia Zeebe de todos los otros workflow engines y habilita scalability horizontal, fault tolerance, y integración nativa con microservicios.
Timeline¶
| Año | Evento |
|---|---|
| 2013 | Camunda 7 lanzado (DB-based, JBoss heritage) |
| ~2015-2017 | Reconocimiento de que microservicios cambiarían el landscape |
| ~2017 | Daniel Meyer inicia diseño de Zeebe (~2 años de design phase) |
| 2017-2019 | Implementación por el equipo de desarrollo (~2 años) |
| 2018 | Rework arquitectónico completo: scalability ✓ pero fault tolerance insuficiente |
| Jul 17, 2019 | Production-ready release anunciado |
| 2020+ | Camunda 8 lanzado, Zeebe es el core |
~4 años desde concepción hasta production-ready — una inversión significativa de R&D que justifica analizar las decisiones cuidadosamente.
El problema que intentaron resolver¶
Cita literal del blog (julio 2019): "microservices and distributed systems are the forks and knives of that digital transformation".
Específicamente, coordinar "decenas a centenas de acciones implementadas por diferentes microservicios" manteniendo visibilidad y control sobre workflows complejos que cruzan múltiples services.
Los workflow engines tradicionales (incluyendo Camunda 7) no fueron diseñados para esto: - Asumen un único monolith con DB compartida - Persistencia transaccional ACID con la DB del negocio - Engine embebido en cada aplicación - Scaling vertical, no horizontal
La decisión decisiva: stream processing > database¶
Cita de Daniel Meyer:
"This simple fact [building on stream processing internally instead of a database] is what makes Zeebe unique and completely different from any other workflow engine."
Lo que esto significa¶
En vez de:
flowchart LR
App[Application] --> Engine[Workflow Engine] --> DB[(SQL Database transactional state)]
Zeebe hace:
flowchart LR
Client[Client] --> Gateway[Gateway]
Gateway --> Broker[Broker]
Broker --> Log[(Append-only log Raft-replicated)]
Broker --> SP[Stream processor single-threaded]
SP --> State[(Local state RocksDB)]
Ver concepts/command-sourcing y concepts/stream-processing para detalles técnicos.
Beneficios habilitados¶
| Beneficio | Cómo lo habilita stream processing |
|---|---|
| Horizontal scalability | Particiones independientes, cada una con su propio stream |
| Fault tolerance | Replicación via Raft del log, no de la DB |
| Native microservices integration | Event-driven en su core, no request/response |
| Deterministic processing | Same commands → same events (replay possible) |
| Atomic state mutations | Log write y state change en una transacción |
| No DB connection pooling issues | State es local al broker |
Trade-offs aceptados¶
| Trade-off | Razón aceptable |
|---|---|
| Single-thread por partición | Predictable, no race conditions, replay determinista |
| State no consultable externamente | Necesita exporters para visibility |
| Operational complexity | Aceptable para workloads críticos a gran escala |
| Eventual consistency en UIs | El delay del export es aceptable para monitoring |
Alternativas rechazadas¶
Implícitamente rechazadas en el design phase:
- Refactorizar Camunda 7: insuficiente. La arquitectura DB-centric era el problema fundamental.
- Usar Kafka como engine: Kafka es un broker, no un stream processor stateful. Faltaría toda la lógica de BPMN y state management.
- Database con horizontal sharding: añade complejidad sin resolver el problema de coordinación cross-service.
- Workflow engine embebido: vulnerabilidades de seguridad (extensions malformadas), tight coupling.
El rework de 2018¶
El blog menciona un rework arquitectónico completo en 2018: - Ya habían logrado scalability excelente - Pero fault tolerance y resilience eran insuficientes - Para un workflow engine mission-critical, fault tolerance es mandatory
Inferencia: este rework probablemente introdujo o refinó: - Raft consensus (vía fork de Atomix) - Snapshot/replay mechanisms - Process banning (rechazar instancias problemáticas para evitar cascadas) - Backpressure (dropping vs buffering)
Ver concepts/raft-consensus y concepts/backpressure.
Validación empírica¶
El caso de Intuit (ver analysis/intuit-production-benchmarks) validó la decisión:
- Process creation: 18.75x más rápido que Camunda 7
- External task latency TP99: 14.4x mejor
- Sustained throughput: 26x más workflows en 10 horas
Esto cuantifica el valor de los 4 años de R&D y justifica el approach radical.
Lecciones para el MVP¶
Decisiones a replicar¶
- Stream processing core: command sourcing + event replay
- Single-threaded processing per partition: simplicidad > paralelismo
- External task pattern: probado a 7,000+ TPS en producción
- Local state (no DB compartida): pero con PostgreSQL en vez de RocksDB para MVP
Decisiones a NO replicar (por ahora)¶
- Raft consensus: complejidad alta, no necesario para single-node MVP
- Multi-partition: añadirlo cuando se necesite scale horizontal
- Atomix fork: fork de protocolos de consenso es costoso de mantener
- Custom storage engine: usar PostgreSQL probado y operacional
Decisión arquitectónica fundamental del MVP¶
El MVP puede mantener la filosofía de stream processing pero usar PostgreSQL como log + state store unificado:
flowchart LR
Client[Client] --> API[API] --> Proc[Processor single-threaded]
Proc -->|same transaction| Log[(command_log INSERT)]
Proc -->|same transaction| State[(process_state INSERT)]
Esto da: - ✓ Command sourcing - ✓ Determinism (con ORDER BY position) - ✓ Atomic log + state - ✗ Replicación (no Raft, depender de PostgreSQL replication) - ✗ Scalability horizontal (single-partition por ahora)
Aceptable para MVP. Escalar después cuando se necesite.
Cita memorable¶
Del blog: "Events are the new first class citizens."
Esta frase captura la esencia del shift. En engines tradicionales, las entidades (process instance, task) son first-class. En Zeebe, los eventos lo son. Las entidades son derivadas de la suma de eventos.
Esta es la diferencia entre CRUD-centric y event-centric thinking.