Saltar a contenido

ADR-015: Skip inbound connectors

  • Status: Accepted
  • Date: 2026-05-14
  • Tags: integration, connectors

Context and Problem Statement

Camunda tiene framework de inbound connectors (Webhook, Kafka consumer, etc.) con lifecycle complex (activate/deactivate, correlation, failure strategies). ¿Replicamos este pattern, o expose APIs directas para que users construyan sus integrations?

Decision Drivers

  • Inbound connector framework es 10x más complejo que outbound
  • Users pueden construir webhook handlers triviales usando API
  • Pattern emerge naturalmente sin abstraction adicional
  • Camunda's CorrelationFailureHandlingStrategy es overengineering para 95% de casos

Considered Options

  1. Skip inbound framework completamente (use REST API)
  2. Build inbound framework full (como Camunda)
  3. Build subset minimal de inbound
  4. Webhook receiver built-in

Decision Outcome

Chosen option: Skip inbound framework completamente porque: - Users escriben sus propios webhook handlers / Kafka consumers - Llaman REST API del engine: POST /v2/messages/{name}/correlate - Cero framework code que mantener - Pattern emerge sin abstraction prematura

Positive Consequences

  • Cero código de framework inbound a mantener
  • Users tienen control total (su language, su framework)
  • No vendor lock-in en webhook reception
  • APIs del engine son ya-suficientes (publish message, broadcast signal)

Negative Consequences

  • Users escriben código boilerplate (webhook handler básico)
  • Sin element templates para Web Modeler
  • Pattern emerging sin guidance puede inconsistent

API patterns para inbound

Webhook → process

User-controlled webhook handler:

// User's app
import express from 'express';
import { WorkflowClient } from '@mvp/sdk';

const app = express();
const client = new WorkflowClient({ apiKey: '...' });

app.post('/webhooks/payment-received', async (req, res) => {
  const { orderId, amount } = req.body;

  await client.publishMessage({
    name: 'payment-received',
    correlationKey: orderId,
    variables: { amount }
  });

  res.status(200).send('ok');
});

10 líneas. Eso es un inbound connector. Cero abstracción.

Kafka consumer → process

import { Kafka } from 'kafkajs';
import { WorkflowClient } from '@mvp/sdk';

const kafka = new Kafka({ brokers: ['kafka:9092'] });
const client = new WorkflowClient({ apiKey: '...' });
const consumer = kafka.consumer({ groupId: 'workflow-handlers' });

await consumer.subscribe({ topic: 'orders' });

await consumer.run({
  eachMessage: async ({ message }) => {
    const order = JSON.parse(message.value.toString());

    await client.publishMessage({
      name: 'order-placed',
      correlationKey: order.orderId,
      variables: order
    });
  }
});

User controls everything: error handling, retry policy, dedup. Standard Kafka patterns work.

Timer → process

Cron job + REST call:

# crontab
0 8 * * * curl -X POST $ENGINE/v2/process-instances \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"bpmnProcessId": "daily-report", "variables": {"date": "today"}}'

Beneficios

Comparado con Camunda's inbound framework:

Aspecto Camunda inbound MVP approach
LOC to maintain ~100K 0 (sólo REST API)
User language Java only (mostly) Any
Activation logic Framework-controlled User-controlled
Failure handling Strategies abstractas Standard practices
Testing Hard (lifecycle) Trivial (HTTP)
Onboarding Learn framework Learn REST API

Cuándo build inbound framework

SI cliente needs: - Element templates en Web Modeler que auto-generan inbound handlers - Lifecycle managed por la plataforma (engine starts/stops listeners) - Centralized retry/failure handling

Para 95% de casos, ninguno aplica. User code wins.

Documentation pattern

# Receiving Events into Workflows

The MVP engine accepts events via REST API. You write the receiver, the engine
handles correlation.

## Pattern: Webhook → Workflow

1. Your service receives webhook from external system
2. Call POST /v2/messages/{name}/correlate
3. Engine correlates to waiting process instance

## Pattern: Kafka → Workflow

1. Your Kafka consumer reads message
2. Call POST /v2/messages/{name}/correlate
3. ...

## Pattern: Timer → Workflow

Use cron + curl, or your scheduler of choice + REST call.

## Templates

[Link to example handlers in different languages]