Itās been quiet in the last few weeks, but thatāll change. Iāve been working in several SaaS platforms, and I want to tell you about one in this newsletter.
150+ Tables, Zero Joins, Almost Zero Cost
The System I want to talk about today has an interesting architecture. It is built using CQRS on Supabase. And to keep things simple, we made some really radical decisions. For example - our backend has no Read-API. What? Makes zero sense, right? Keep on reading if you want to know what that means.
The system is a Monolith, running on the JVM (Kotlin and Spring), built with a Team of 4, one deployable, 240 independent slices. 150+ tables. 90% AI-generated code. And our server just sits there, waiting for something to do.
Iāve been on the JVM my entire career - Kotlin, Spring, Axon for event sourcing and CQRS. But hereās the controversial part that makes most developers look at me like Iām crazy.
Why Event Sourcing? Because It Is So Much Simpler Than CRUD
I know. You donāt believe me. Nobody does at first.
But hereās what Iāve learned: CRUD systems look deceptively simple upfront. Then the complexity creeps in. Inevitably. You start with a clean schema, some basic endpoints, and everything feels great. Six months later, youāre drowning in join tables, cached denormalized views, and business logic scattered across three layers of your application.
Event sourcing gives you flexibility thatās unseen in traditional architectures. So when we started building our SaaS platform, the decision was clear: we wonāt fall into the CRUD trap.
The PII Problem (And Our Elegant Solution)
Of course, event sourcing brings its own challenges. The big one: how do you handle PII in an immutable event log?
Whatās the PII Problem? We need to be very careful how we handle data (as in every architecture). And if a User requests data to be removed, we need to be able to do so.
But how do you handle this with an immutable list of events like in Event Sourcing? We canāt just delete Events. That breaks the whole point. But legally, you might have to delete personal data.
Our solution: crypto shredding.
We built a simple functionality that plugs into serialization of the Event Store - it encrypts PII-relevant data and stores the keys in Supabase Vault. When someone requests data deletion, we donāt touch the events. We just destroy the encryption key. The dataās still in the event log technically, but itās permanently unreadable.
The Most Radical Decision: No Read API
Hereās where most developersā heads explode.
There is no read API in our server.
None. Zero.
The āread APIā is direct table access - clients query Supabase tables directly.
Why We Did It: Multi-Tenancy at the Database Layer
The driver was multi-tenancy. We handle 100% of tenant isolation at the database layer using Row Level Security (RLS) policies.
Our application layer knows nothing about multi-tenancy. Literally nothing.
A developer writes SELECT * FROM persons and it automatically returns only the persons for that tenant. The RLS policy states: āclients can only select persons which belong to the assigned tenant.ā
Zero application-level filtering. Super simple.
But it feels wrong.
Weāve been taught for decades NOT to give clients direct database access. It goes against everything we learned. And of course, if your application isnāt structured for this, it creates tons of problems.
For example: there is not a single join in our system. Now the rest of the heads exploded I guess. As I said, we made some radical decisions.
The āNo Joinsā Constraint
Not one join. Anywhere.
Why? Because with CQRS and direct database access, weāre building dedicated, denormalized projections for each view. Every view has its own tailor-made table structure.
Thereās literally no need for joins in 90% of cases.
The other 10%? We simply make 2 queries. Thatās something we accept.
The key insight: the Event Model shows you exactly what structure each projection needs.
Youāre not guessing. Youāre not doing the classic ānormalize first, denormalize later when performance sucksā dance. You model the view requirements upfront, and the projection structure is obvious.
The Iron-Clad Development Rule
We donāt write a single line of code before we model it.
This isnāt a preference. Itās discipline. Almost religious conviction.
Why?
Because Iāve been burned too many times by ājust this small fix.ā
You know the pattern. Something seems simple. Thirty minutes, max. You skip the modeling because itās obvious, right? Then one change begets the next. What you thought was a small fix cascades into a major refactor. Days or weeks later, youāre rebuilding half the system.
The Event Model shows you those ripple effects BEFORE you write code.
Getting burned is the best way to learn, unfortunately.
And I got burned enough times to make this rule absolute. I simply decided not to get burned anymore.
UI Without Logic
Our frontend is React with very simple components.
Since the data is tailor-made in projections, thereās literally no logic in the UI. Just:
- Select from table
- Show the data
- Fire off commands
Thatās it.
The Game-Changer: AI-Generated Code
Hereās where it gets interesting: This allows us to generate 80-90% of our UI.
āBut Martin,ā youāre thinking, āAI-generated code is usually garbage.ā
Youāre right. In traditional architectures, it is.
But with our approach? Even for AI, It feels unnatural to structure it wrong.
Using the sliced approach and based on tailor-made projections, there is literally no other way than to do it right. Even for AI.
The architecture constrains everything:
- Event Model defines the projections
- Projections define clean, purpose-built data structures
- AI generates components from clean structures
- The system almost canāt produce bad code
Weāre at 90% generated now. Weāll hit 100% soon.
The Architecture at Scale
Our system has 240 independent slices (give or take).
Each slice can be deleted without affecting the others. Yes, we pay with redundancy - weāre working with 150+ tables. But hereās the thing:
Each slice is one consistency boundary. Like a tiny microservice.
Itās all one monolithic deployment. There is simply no need to physically distribute it.
Best of both worlds. The isolation and independence of microservices, without the operational nightmare of deploying 240 separate services.
Managing the Madness
āBut how do you manage 150+ tables without going insane?ā
The answer: most changes are isolated to a small set of slices. When multiple slices ARE affected, the Event Model shows you the dependencies exactly. No surprises. No hidden coupling.
Despite having 240 slices, the system feels lightweight.
The āBoredā Server
Hereās my favorite part: our system is bored most of the time.
Itās a write engine. Process commands. Emit events. Update projections. 90% of the system is reading. All those queries never touch our system. This is completely handled by Supabase and Postgres. Hands off.
Thatās it.
Supabase and Postgres handle all the heavy lifting. Our application server just⦠waits.
The result? The whole system runs at almost no cost.
We could handle 100x the load without even noticing.
The Two-Day Startup
This architecture isnāt just elegant. Itās a startup factory.
Our first system took 3 months to build. We were figuring out the patterns, establishing the approach, learning what worked. Now this is our blueprint.
The second system? From 3 months to two days.
- Day 1: Event Modeling - very focused, small group
- Day 2: Building (100% generated code)
Two days. Complete, production-ready SaaS.
Both are launching in March - the first one on March 1st, targeting a very specific market niche in one country. The second follows shortly after.
What This All Means
This whole model works beautifully for new systems - and is incredibly fast.
But it works equally well for existing systems.
Are you planning a legacy modernization right now? Or are you in the middle of one?
As for the system I described in this newsletter, good solutions start with a clear understanding of the problem.
Most modernizations fail before they even start - death by requirement gaps. Hidden business rules buried in legacy code. Assumptions nobody documented. Handoffs that worked in the old system but nobody can quite explain why.
Join the Agentic Engineer Program
Apply Spec-Driven Development Hands-On - Event Modeling, Event Sourcing, and AI Engineering with autonomous agents.
Book a Call Today
Want to talk through how Event Modeling could work for your team or project? Letās have a quick, no-pressure conversation.
