Layer 2: EIP Overview
Layer 2 is Priostack's Enterprise Integration Patterns (EIP) routing layer. It operates on top of the BPMN execution engine (Layer 1) to provide message-oriented integration primitives for complex, large-scale workflow scenarios.
Architecture Diagram
External Systems / APIs · Messages (JSON/XML/binary)
v
┌────────────────────────────────────────────────────────┐
│ LAYER 2: EIP │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Message │────►│ Message │ │
│ │ Channel A │ │ Router │──► Channel B │
│ └─────────────┘ │ (FEEL) │──► Channel C │
│ └─────────────┘──► Channel D │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Splitter │────►│ Pipeline │ │
│ │ (fan-out) │ │ (filter → │ │
│ └─────────────┘ │ translate)│ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Aggregator │ │ Correlation│ │
│ │ (collect N)│ │ Context │ │
│ └─────────────┘ │ (dedup) │ │
│ └─────────────┘ │
│ │ │
│ ┌────────────▼────────────┐ │
│ │ Message Endpoint │ │
│ │ (triggers BPMN start) │ │
│ └────────────────────────┘ │
└────────────────────────────────────────────────────────┘
│
v
┌────────────────────────┐
│ LAYER 1: BPMN │
│ Execution Engine │
└────────────────────────┘
Patterns Reference
Message Channel
A named, typed conduit through which messages flow from producers to consumers. Channels decouple message sources from destinations. Each channel has a defined schema for the messages it carries.
// Configure a channel
{
"name": "order-events",
"schema": "application/json",
"retention": "7d",
"maxQueueDepth": 10000
}
Message Router
Routes incoming messages to one or more output channels based on FEEL expressions. The router evaluates conditions against the message payload and headers to determine the routing decision.
// Route by order amount
{
"type": "router",
"input": "order-events",
"routes": [
{ "condition": "=amount > 10000", "output": "high-value-orders" },
{ "condition": "=amount > 1000", "output": "standard-orders" },
{ "default": true, "output": "small-orders" }
]
}
Aggregator
Collects a set of related messages and combines them into a single result message. The aggregator groups messages by a correlation key and emits a combined result when a completion condition is met (e.g., all N messages received, or a timeout expires).
// Aggregate all order lines before processing
{
"type": "aggregator",
"correlationKey": "=orderId",
"completionCondition": "=count(messages) >= lineCount",
"timeout": "PT5M",
"output": "order-ready"
}
Correlation Context
Maintains a time-windowed deduplication context for messages. If the same message (identified by event ID or a FEEL key expression) is received multiple times within the window, only the first occurrence is processed. Subsequent duplicates are silently dropped.
// Deduplicate by payment transaction ID
{
"type": "correlation-context",
"key": "=transactionId",
"window": "PT1H",
"onDuplicate": "drop"
}
Pipeline
A multi-stage processing chain where each stage is either a filter (drops messages not matching a predicate) or a transformer (modifies message content). Stages are applied sequentially.
// Filter → validate → enrich pipeline
{
"type": "pipeline",
"stages": [
{ "filter": "=status != 'test'" },
{ "transform": "=context merge(message, { processedAt: now() })" },
{ "filter": "=amount > 0" }
],
"output": "validated-orders"
}
Splitter
Takes a single message containing a list and emits one message per list item. Useful for fan-out scenarios where a batch message must be processed item by item.
// Split an order batch into individual orders
{
"type": "splitter",
"expression": "=orders",
"correlationId": "=batchId",
"output": "individual-orders"
}
Message Translator
Transforms a message from one schema to another using a FEEL context expression. Field names, types, and structure can be remapped. Used to bridge differences between upstream and downstream message formats.
// Translate from legacy order format to canonical format
{
"type": "translator",
"mapping": {
"orderId": "=legacy.order_no",
"customerId": "=legacy.cust_id",
"amount": "=decimal(legacy.amount_cents / 100, 2)",
"currency": "=upper case(legacy.curr)"
}
}
Message Filter
A simple single-stage filter that drops messages not matching a FEEL predicate. Equivalent to a one-stage Pipeline with a single filter step.
// Only pass orders from the EU region
{
"type": "filter",
"predicate": "=region = \"EU\" and currency in [\"EUR\", \"GBP\", \"CHF\"]",
"onReject": "dead-letter"
}
Message Endpoint
Bridges Layer 2 channels to Layer 1 BPMN processes. When a message arrives on the input channel, the endpoint either starts a new process instance or correlates the message to an existing waiting instance (via a correlation key).
// Start a BPMN process for each order on the channel
{
"type": "endpoint",
"input": "validated-orders",
"action": "start-process",
"bpmnProcessId": "order-processing",
"variableMapping": {
"orderId": "=message.orderId",
"amount": "=message.amount",
"customerId": "=message.customerId"
}
}
// Or correlate to a waiting Message Catch Event
{
"type": "endpoint",
"input": "payment-confirmed",
"action": "correlate-message",
"messageName": "payment-received",
"correlationKey": "=message.orderId"
}
Integration with BPMN
Layer 2 patterns connect to Layer 1 processes through Message Endpoints. From the BPMN perspective, Layer 2 is transparent — a process simply waits at a Message Catch Event or is started by an external trigger. The routing and transformation logic lives entirely in Layer 2 configuration, keeping BPMN diagrams clean.
See the Architecture page for the full two-layer system diagram and how the layers interact.