Search Infrastructure en Camunda 8¶
Resumen: Camunda 8 implementa una capa de búsqueda backend-agnostic que abstrae Elasticsearch, OpenSearch y RDBMS detrás de una misma interfaz. El patrón arquitectónico es Controllers → Service → SearchQueryRequestMapper → TypedSearchQuery → SearchClientReader → ES/OS → ResponseMapper. La infraestructura se organiza en 9 sub-módulos Maven con ~40 readers en el módulo client-reader, soportando todos los tipos de entidades del sistema.
Motivación: abstracción backend-agnostic¶
Problema¶
Camunda 8 necesita consultar datos de runtime (process instances, jobs, incidents, variables, user tasks, etc.) para sus webapps (Operate, Tasklist) y su REST API. Originalmente, esto se hacía directamente contra Elasticsearch, pero:
- Lock-in: depender directamente de Elasticsearch acopla la arquitectura a un vendor específico.
- OpenSearch: AWS promueve OpenSearch como fork; muchos clientes lo usan.
- RDBMS: para instalaciones simples o on-premise, una base de datos relacional puede ser suficiente sin la complejidad de un cluster ES/OS.
- Testing: ejecutar tests contra ES/OS real es lento; una abstracción permite mocks o in-memory backends.
Solución¶
Una capa de abstracción que define interfaces agnósticas al backend, con implementaciones pluggables para cada motor de búsqueda.
Patrón arquitectónico¶
El flujo completo de una query de búsqueda sigue este pipeline:
flowchart TD
Ctrl[REST Controller] --> Svc[Service Layer]
Svc --> Mapper[SearchQueryRequestMapper]
Mapper -->|"Convierte request<br/>→ TypedSearchQuery"| TSQ[TypedSearchQuery]
TSQ -->|"Representación interna,<br/>backend-agnostic"| Reader[SearchClientReader]
Reader -->|"Traduce a query del backend"| Backend[(Backend:<br/>ES / OpenSearch / RDBMS)]
Backend --> Resp[ResponseMapper]
Resp -->|"Convierte response<br/>→ modelo de dominio"| DTO[DTO de respuesta]
Componente por componente¶
Controllers¶
Los controllers REST exponen los endpoints de búsqueda:
GET /v2/process-instances?filter.state=ACTIVE&sort=startDate&size=20
POST /v2/process-instances/search (body con filtros complejos)
- Reciben parámetros de búsqueda (filtros, sort, pagination).
- Validan el input.
- Delegan al service layer.
- No conocen el backend de búsqueda.
Service Layer¶
Orquesta la lógica de negocio de la búsqueda:
- Aplica authorization checks (ver concepts/authorization-model).
- Aplica tenant filtering (ver concepts/multi-tenancy).
- Transforma filtros de negocio en queries de búsqueda.
- Maneja paginación y sorting.
SearchQueryRequestMapper¶
Convierte el request del controller al modelo interno de búsqueda:
- Mapea campos del request a campos de filtro.
- Convierte sort specifications.
- Aplica defaults (tamaño de página, sort default).
TypedSearchQuery¶
La representación interna y backend-agnostic de una query:
TypedSearchQuery<F extends FilterBase, S extends SortOption> {
filter: F, // criterios de filtrado
sort: List<S>, // criterios de ordenamiento
page: SearchAfter, // paginación (search_after pattern)
size: int // tamaño de página
}
- Genérica: parametrizada por tipo de filtro y tipo de sort.
- Inmutable: una vez creada, no se modifica.
- Sin referencia a backend: no contiene DSL de ES ni SQL.
SearchClientReader¶
El corazón de la abstracción. Traduce TypedSearchQuery al lenguaje nativo del backend:
// Para Elasticsearch
TypedSearchQuery → SearchRequest (ES Java client) → ES → SearchResponse
// Para OpenSearch
TypedSearchQuery → SearchRequest (OS Java client) → OS → SearchResponse
// Para RDBMS
TypedSearchQuery → SQL query → JDBC → ResultSet
- Cada backend tiene su implementación del
SearchClientReader. - ~40 readers distintos cubren todos los tipos de entidades.
ResponseMapper¶
Convierte la respuesta del backend al modelo de dominio:
Los 9 sub-módulos Maven¶
La infraestructura de búsqueda se organiza en 9 módulos Maven, cada uno con una responsabilidad clara:
1. search-domain¶
- Define los modelos de dominio de búsqueda.
- Interfaces de filtros, sorts, y queries.
- No tiene dependencias de backend.
2. search-client¶
- Define las interfaces de los clients de búsqueda.
SearchClientReaderinterface.SearchClientWriterinterface (para indexación).
3. search-client-elasticsearch¶
- Implementación del
SearchClientReaderpara Elasticsearch. - Usa el Java client oficial de Elasticsearch (
co.elastic.clients:elasticsearch-java). - Traduce
TypedSearchQueryaSearchRequestde ES. - Maneja connection pooling, retry, y serialización.
4. search-client-opensearch¶
- Implementación del
SearchClientReaderpara OpenSearch. - Usa el Java client oficial de OpenSearch (
org.opensearch.client:opensearch-java). - API muy similar a ES client (OpenSearch es fork), pero con diferencias en features avanzados.
5. search-client-reader (~40 readers)¶
- Contiene las implementaciones concretas de readers para cada tipo de entidad.
- ~40 readers, uno por cada entidad consultable del sistema.
Readers por categoría:
| Categoría | Readers |
|---|---|
| Process | ProcessDefinitionReader, ProcessInstanceReader, FlowNodeInstanceReader, VariableReader, SequenceFlowReader |
| Decision | DecisionDefinitionReader, DecisionInstanceReader, DecisionRequirementsReader |
| User Tasks | UserTaskReader, FormReader |
| Jobs | JobReader |
| Incidents | IncidentReader |
| Messages | MessageReader, MessageSubscriptionReader |
| Operations | BatchOperationReader, OperationReader |
| Identity | UserReader, GroupReader, RoleReader, TenantReader, AuthorizationReader, MappingRuleReader |
| Infrastructure | DeploymentReader, ResourceReader |
Cada reader implementa: - Mapping de campos de filtro a campos del índice/tabla. - Mapping de sort options a campos ordenables. - Conversión de respuestas al modelo de dominio. - Paginación eficiente (search_after para ES/OS, OFFSET/LIMIT para RDBMS).
6. search-query-transformer¶
- Transformaciones y optimizaciones de queries antes de enviarlas al backend.
- Rewriting de filtros para eficiencia.
- Normalización de sorts.
- Aplicación de filtros de autorización y tenant.
7. search-plugin¶
- Sistema de plugins para extender la funcionalidad de búsqueda.
- Permite agregar filtros custom, transformaciones, o post-procesamiento.
8. search-connect¶
- Configuración y conexión a los backends.
- Connection factories para ES, OS, RDBMS.
- Health checks del backend.
- Configuración de indices/aliases/templates.
9. search-rdbms (implícito)¶
- Implementación para bases de datos relacionales.
- Usa Spring Data JPA / JDBC.
- Soporta PostgreSQL, H2 (para testing).
Readers especializados (~40)¶
Existen 40+ reader classes, cada uno especializado en un tipo de entidad:
- ProcessInstanceReader — instancias con state, incidents, tree path.
- FlowNodeInstanceReader — elementos BPMN ejecutados.
- VariableReader — variables con scoping.
- IncidentReader — incidents activos y resueltos.
- JobReader — estado de jobs.
- DecisionInstanceReader — evaluaciones DMN.
- UserTaskReader — user tasks con assignment.
- ProcessDefinitionReader — definiciones deployadas.
Cada reader encapsula la lógica de mapping específica de su entidad, incluyendo joins denormalizados (como el operate-list-view que usa join relations en ES).
Paginación: search_after pattern¶
La búsqueda usa el patrón search_after en lugar de offset-based pagination:
Problema con offset¶
SELECT * FROM process_instances ORDER BY start_date LIMIT 20 OFFSET 10000
-- Problema: el backend debe leer y descartar 10000 rows
Solución: search_after¶
// Request 1: primera página
{sort: [{field: "startDate", order: "DESC"}], size: 20}
→ Resultados: [item_1, item_2, ..., item_20]
→ searchAfter: ["2026-05-14T10:00:00", "12345"]
// Request 2: siguiente página
{sort: [{field: "startDate", order: "DESC"}], size: 20, searchAfter: ["2026-05-14T10:00:00", "12345"]}
→ Resultados: [item_21, item_22, ..., item_40]
Ventajas: - Rendimiento constante: no importa qué tan profundo se pagine. - Consistencia: si se insertan datos entre requests, no se pierden ni duplican items. - Compatible con ES/OS: es el mecanismo nativo de paginación eficiente.
Backend RDBMS (8.8+)¶
Desde la versión 8.8, Camunda soporta PostgreSQL como backend de búsqueda alternativo a Elasticsearch. Esto simplifica significativamente el deployment:
- Elimina la dependencia de ES/OS para instalaciones pequeñas.
- Usa el mismo PostgreSQL que Identity y el RDBMS exporter.
- Queries SQL estándar con índices apropiados.
- Trade-off: sin full-text search avanzado ni aggregations complejas.
Índices de Elasticsearch¶
Los índices principales que alimentan las webapps:
| Índice | Contenido | Consumidor |
|---|---|---|
operate-list-view |
Process instances + flow nodes + variables (join relation) | Operate |
operate-incident |
Incidents activos y resueltos | Operate |
tasklist-task |
User tasks con assignment y forms | Tasklist |
zeebe-record-* |
Raw events exportados por ElasticsearchExporter | Optimize |
operate-operation |
Batch operations | Operate |
operate-decision-instance |
Evaluaciones DMN | Operate |
El índice operate-list-view es particularmente complejo: usa join relations de ES donde processInstance es el parent y activity + variable son children. Esta denormalización permite queries eficientes pero complica la migración entre versiones.
Consideraciones para un MVP¶
- Esencial: una abstracción de búsqueda, aunque sea mínima, evita acoplarse a un backend específico. Incluso si el MVP solo soporta un backend, definir interfaces ahora facilita agregar otros después.
- MVP mínimo: implementar solo el backend de Elasticsearch (o PostgreSQL para simplicidad) con 5-10 readers para las entidades esenciales (ProcessInstance, Job, Incident, Variable, ProcessDefinition).
- Simplificable: los 9 sub-módulos pueden consolidarse en 2-3 para un MVP: domain, client-impl, readers.
- Simplificable: search_after pagination puede diferirse a favor de offset-based para un MVP (aceptando la limitación de rendimiento en paginación profunda).
- Recomendado: el patrón TypedSearchQuery como representación intermedia es una buena inversión desde el inicio — es lo que permite agregar backends sin reescribir la lógica de negocio.
- Recomendado: cada reader como clase independiente (en lugar de un mega-reader) facilita testing y mantenimiento.
- Alternativa simplificada: para una plataforma simplificada, la search infrastructure entera se puede reemplazar con queries SQL directas contra PostgreSQL. Los 9 módulos y 40+ readers se reducen a un repositorio SQL por entidad.
Ver analysis/trade-offs-arquitectonicos para el análisis de la decisión ES vs PostgreSQL.