Stories · Season 2 — Frontrow · Episode 09

The Partner Who Spoke a Different Language

In which Frontrow lands a huge partnership, and discovers the partner runs on a message broker Frontrow has never heard of.

The story

Frontrow signed its biggest deal yet: a national venue chain would list all its events through Frontrow. Tobi was thrilled. Then the partner’s integration team sent over their requirements, and Mara’s stomach dropped. The partner published events over Kafka. Frontrow’s entire messaging stack was built on RabbitMQ — hard-coded, woven through every consumer, assumed everywhere.

Worse, the next partner in the pipeline used Azure Service Bus, because they were an all-Microsoft shop. And an experimental integration with a logistics vendor for physical ticket fulfillment wanted SQS. Frontrow was about to be fluent in nothing and conversational in four different brokers.

Mara looked at the codebase, where “send a message” meant “call this specific RabbitMQ client,” repeated in dozens of places. Adding Kafka the obvious way meant threading a second messaging client through all of it, then a third for Service Bus, then a fourth. “We’re going to end up,” she said, “with four copies of every consumer, one per broker, and a different set of bugs in each.”

Why this is hard the traditional way

Most systems don’t have a messaging layer; they have a specific broker’s client library smeared across the code. “Publish this event” is written as “call RabbitMQ,” and the broker’s particular quirks — how it acknowledges, how it retries, how it dead-letters — leak into every consumer.

That works fine until the day you need a second broker, and then the cost lands all at once. Every place that touched the broker has to learn a new one. Each broker handles delivery, ordering, and failure differently, so you don’t get one new integration — you get a combinatorial explosion of broker-specific edge cases, each tested (if you’re lucky) separately.

How Relay changes the ending

Relay puts your code behind broker-neutral abstractions — IMessageBroker and IMessageConsumer — and provides the actual transports (RabbitMQ, Kafka, Azure Service Bus, AWS SQS/SNS, NATS, and more) as pluggable implementations. Your consumers are written once, against the abstraction. Which broker carries the message is configuration, not code.

// Your consumer doesn't know or care which broker delivered this.
public sealed class VenueEventConsumer : IMessageConsumer<VenueEventListed> { /* ... */ }

// The partner speaks Kafka; the next one speaks Service Bus. That's a config choice.
builder.Services.AddRelayKafka(...);          // national venue chain
builder.Services.AddRelayAzureServiceBus(...); // the all-Microsoft partner

Now “support a new partner’s broker” is a registration line, not a rewrite. And because every transport is exercised against the same conformance test suite, the Kafka path behaves like the RabbitMQ path you already trust — same delivery guarantees, same dead-lettering, same retries — instead of being a fresh source of surprises. Frontrow gets to say yes to partners regardless of what they run.

What it costs you to ignore this

  • Your tech choices become a sales blocker. “We can’t integrate unless you use our broker” is a great way to lose an enterprise deal.
  • Each new transport multiplies the code. Broker-specific consumers breed broker-specific bugs, in parallel.
  • Migration becomes a rewrite. Outgrowing your first broker means touching everything that ever sent a message.
  • You’re locked in. When the broker’s pricing or limits stop working for you, switching costs as much as the original build.