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.