Zero external dependencies

Add integration pipelines without a broker

Layer 2 is Priostack's embedded EIP integration layer — 9 Enterprise Integration Patterns running inside your BPM runtime. No Kafka, no RabbitMQ, no ops overhead.

Start building → Read the docs →

9 integration patterns, built in

Every pattern ships as a typed Go struct. No XML, no DSL files, no external bus — just import the package and wire your workflow.

📡

MessageChannel

Point-to-point and pub-sub message delivery between components

🔌

MessageEndpoint

Send and receive messages with correlation key support

🔀

MessageRouter

Route messages to different channels via FEEL expressions

🔍

MessageFilter

Drop or queue messages that don't match a predicate

📦

Aggregator

Collect related messages and fire a callback when a batch completes

✂️

Splitter

Split a list variable into parallel messages for fan-out

🔄

MessageTranslator

Map and transform message fields using FEEL expressions

⛓️

Pipeline

Chain filters in sequence — Pipeline.Run(ctx, msg)

🎯

CorrelationContext

First-match-wins deduplication with configurable TTL

5 Idempotency Mechanisms

Choose the right deduplication strategy for your latency and persistence requirements. All five work without an external broker.

Mechanism Scope Storage When to use
CorrelationContext Per-channel In-memory High-speed dedup within a session
MessageID check Global Persistent Cross-restart exactly-once
Database upsert Application PostgreSQL Business-level idempotency
Conditional routing Pipeline None Skip duplicates at router
Acknowledgement timeout Endpoint In-memory At-least-once with dedup

Works exactly as you expect

Every primitive is a Go struct — no config files, no annotations, no framework lock-in.

// Create a pub-sub channel for the credit-check stage
ch := layer2.NewMessageChannel("credit-check", layer2.PubSub)
ch.Publish(ctx, layer2.Message{Key: loanID, Body: applicant})
ch.Subscribe(func(msg layer2.Message) {
    result := creditService.Evaluate(msg.Body.(Applicant))
    msg.Reply(result)
})
// Aggregate 100 loan applications before risk assessment
agg := layer2.NewAggregator("loan-batch",
    layer2.CorrelationKey("portfolio_id"),
    layer2.CompletionCondition("count(messages) >= 100"),
    layer2.OnComplete(func(batch []layer2.Message) {
        risk := riskEngine.AssessBatch(batch)
        downstream.Publish(ctx, risk)
    }),
)
// Dedup incoming messages, then run the credit pipeline
ctx := layer2.NewCorrelationContext(
    layer2.TTL(5 * time.Minute),
    layer2.FirstMatchWins(),
)
pipeline := layer2.NewPipeline(
    ctx.Filter(),       // drop duplicates
    creditFilter,       // business logic
    translatorStep,     // transform output
)
pipeline.Run(ctx, incomingMsg)

Layer 2 vs External Brokers

External brokers solve hard distribution problems — but most enterprise workflows don't need them. Layer 2 removes the accidental complexity.

vs Kafka

Kafka adds 3+ brokers and days of config

Kafka requires 3+ brokers, ZooKeeper or KRaft, topic management, consumer group coordination, and a dedicated ops team. It solves large-scale log distribution — not in-process workflow messaging.

Layer 2: embedded, zero config, starts in <1 ms
vs RabbitMQ

RabbitMQ needs a server before line one of code

RabbitMQ needs a dedicated server, an AMQP client library, exchange and binding configuration, and a connection string in every service. Infrastructure overhead precedes the first message.

Layer 2: import one package, no infrastructure
vs AWS SQS

SQS adds latency and a billing line-item per message

SQS charges per API call, adds 50–200 ms of network latency on every message, and requires AWS credentials, SDK configuration, and IAM policies before you can enqueue anything.

Layer 2: sub-millisecond, zero cost per message

Up and running in 4 steps

No YAML manifests. No Docker containers. No service mesh. Just Go code that compiles and runs.

1

Create a MessageChannel

Instantiate a named channel in the channel registry — point-to-point or pub-sub.

2

Publish from a service task

Call ch.Publish(ctx, msg) from any BPMN service task output handler.

3

Consume in a worker

Register a subscriber function. It receives typed messages and can reply inline.

4

Add CorrelationContext for dedup

Wrap your pipeline with a CorrelationContext to drop duplicate messages before they reach business logic.

Layer 2 starter templates

Deploy a fully wired integration pattern into your workspace in one click. Each template ships with a BPMN diagram, Go worker, and unit test.

Aggregator pattern

Batch Loan Processing

Collect loan applications by portfolio ID and trigger a risk batch once the batch size threshold is met.

Deploy template →
MessageRouter pattern

Risk Level Routing

Route loan decisions to low-risk auto-approval, manual review, or hard-decline channels via a FEEL expression.

Deploy template →
CorrelationContext pattern

Event Deduplication

Drop duplicate webhook events within a configurable TTL window before they enter the processing pipeline.

Deploy template →
MessageChannel pub-sub

Fan-out Notifications

Broadcast a process completion event to audit, email, and analytics subscribers simultaneously.

Deploy template →
MessageTranslator + Pipeline

Pipeline Transform

Chain a FEEL-based field mapper and a schema validator in a single pipeline stage before downstream delivery.

Deploy template →

Common questions

If your question isn't here, reach out to the team via the console or the community forum.

Can I use Layer 2 without Layer 1 (BPMN)?

Yes. Layer 2 is a standalone Go package. You can import layer2 independently of the BPMN runtime and use message channels, pipelines, and aggregators in any Go application.

Does it require a message broker?

No. By default, all message channels run in-process using Go channels and sync primitives. There is no TCP socket, no serialization overhead, and no external dependency to deploy or manage.

Can Layer 2 talk to Kafka or RabbitMQ?

External broker adapters are on the roadmap. The planned API will let you swap the transport backend of any MessageChannel from in-process to Kafka or AMQP without changing application code.

How do I persist messages across restarts?

A PostgreSQL-backed channel store is currently on the roadmap. In the interim, use the MessageID check mechanism against a persistent store to achieve cross-restart exactly-once semantics in your worker.

What is the throughput?

In internal benchmarks, a 5-stage pipeline processes 10,000 messages per second on a single core with p99 latency under 200 microseconds. Results vary with payload size and filter complexity.

Join the Layer 2 Trailblazers

47 architects have already deployed integration pipelines — without a single external broker.

MR
Marcus R.
Principal Architect, Fintech
"We replaced a 3-broker Kafka cluster for our internal loan pipeline with Layer 2 in an afternoon. Ops cost dropped to zero and latency halved."
SL
Sofia L.
Lead Engineer, Insurance
"The Aggregator pattern is exactly what we needed for batch claims processing. The completion condition in FEEL made the business rule readable to the whole team."
DK
David K.
BPM Consultant
"CorrelationContext with first-match-wins solved our webhook deduplication problem in five lines of code. I've been recommending Layer 2 to every client since."