Why Monesize Core Uses No Kafka or Redis

One of the most common patterns in software engineering is solving reliability problems by introducing more infrastructure.

Need background jobs? Add Redis. Need reliable event delivery? Add Kafka. Need asynchronous processing? Add a queue. Need distributed messaging? Add another service.

Sometimes that is the right decision. Sometimes it is not.

Recently, I implemented the Event Outbox pattern across Monesize Core to guarantee reliable cross-module event delivery. The obvious question is: why not just use Redis, Kafka, RabbitMQ, or BullMQ?

The answer comes down to architecture.

Monesize Core is not a typical multi-tenant SaaS platform. Every customer receives their own isolated deployment. Their own application environment. Their own database. Their own infrastructure footprint.

This architectural decision has implications.

Every new infrastructure dependency added to the platform becomes a dependency that must exist in every customer deployment. 

Introducing Redis would not simply mean operating one Redis cluster. It would mean every deployment now requires provisioning, configuration, monitoring, backup strategies, security hardening, version management, incident response, and operational maintenance.

At one customer, this is manageable. At fifty customers, it becomes noticeable. At hundreds of customers, it becomes a permanent operational burden.

This is where engineering decisions become trade-offs rather than technical exercises.

The question is not "Can Redis solve this problem?" Of course it can. The question is "Does this problem require Redis?" Those are different questions.

When I looked at the reliability problem I was trying to solve, I realized something important. The database already contained everything I needed.

Transactions already existed. Persistence already existed. Row-level locking already existed. Retry state could be stored in tables. Dead-letter handling could be stored in tables. Durability already existed.

Why introduce another moving part if the existing infrastructure could already solve the problem?

The failure mode I wanted to eliminate was straightforward. A business transaction succeeds. The database commits. The application attempts to emit an event. The process crashes. The event disappears. One module believes something happened. Another module never receives the message. The system slowly drifts out of sync.

For operational software, that is unacceptable.

The solution I implemented writes events to an Event Outbox table inside the same database transaction as the business operation itself. If the transaction commits, the event exists. If the event exists, a background poller will eventually process it. If processing fails, retries occur automatically. If retries are exhausted, the event moves into a failed state where it becomes visible and recoverable.

The system now has reliable event delivery without requiring additional infrastructure. No Redis. No Kafka. No RabbitMQ. No separate queue service. No additional maintenance burden for future deployments.

Some engineers equate architectural sophistication with the number of technologies involved. I tend to think the opposite.

Good architecture is not about introducing components. It is about solving problems with the smallest number of moving parts necessary.

Every dependency is a future maintenance obligation. Every service added today becomes something that must be monitored, upgraded, secured, and supported tomorrow. Removing infrastructure is often harder than adding it.

Adding Kafka would have been easy. Justifying why Kafka was truly necessary would have been harder. In this case, I could not justify it. The database was already capable of providing the guarantees I needed.

The simplest solution turned out to be the most operationally efficient one.

As Monesize Core grows, I expect many future engineering decisions to follow the same principle. Not every problem deserves another service. Not every reliability challenge requires more infrastructure. Sometimes the best engineering decision is the one that leaves you with fewer things to manage.

Comments

Popular Posts

Building Event Reliability at Monesize Core

RecruitX: From Reconnaissance to Remote Code Execution

God Never Wrote a Book: A Nigerian Agnostic's Case