I quit my job, burned my savings, and built a core banking from scratch. Here's what I learned.
I walked away from a CTO seat, burned through my savings, and spent four years building a core banking system that the industry said was impossible. Event Sourcing, native ISO 20022, BIAN, Kafka — from the ground up. No wrappers. No shortcuts. This is what happened.

Everyone told me not to do this
In late 2022, I was sitting in a corner office on Faria Lima — São Paulo's Wall Street — with a title that took me a decade to earn. CTO of an autonomous investment agent under XP, one of Latin America's largest brokerages. Stock options vesting. A team I'd handpicked. The kind of gig you don't walk away from.
I walked away from it.
The idea was simple and, in hindsight, a little insane: build a securitization company. Tokenize receivables, connect small originators to capital markets, blow open access to credit for businesses that banks wouldn't touch. I had the thesis. I had the conviction. What I didn't have was the infrastructure to make it real.
The six months that radicalized me
To build a securitization platform, you need banking rails. Accounts. Payments. A ledger. Risk scoring. Compliance. The obvious move: plug into a BaaS provider and start building.
So I went shopping. I talked to every Banking-as-a-Service provider in Brazil. I signed NDAs. I sat through demos. I read documentation — when they let me.
What I found still makes my blood boil.
You had to sign a commercial contract just to read the API docs. Not a sandbox agreement. A binding contract — with minimum volume commitments, exclusivity clauses, and penalties. You couldn't even *evaluate* the product without being locked in. Imagine buying a car you're not allowed to test drive.
The APIs were a dialect, not a language. Every provider had invented their own message format. No ISO 20022. No BIAN. No consistency. Learn one, and that knowledge was worthless everywhere else. It was as if every toll road in the country had its own currency.
Behind the shiny REST endpoints: COBOL. The modern API facades were spray-painted over batch-processing cores from the 1990s. Real-time reconciliation? Impossible. Settlement before end-of-day? A feature request, not a capability. Eighty percent of the time, the APIs simply couldn't do what the market needed *today*.
Support was a Slack channel where messages went to die. No SLAs. No incident reports. No one to call at 2 AM when a payment was stuck in limbo. You were on your own.
Six months. Dozens of providers. Zero that could support what I was building.
The bet that terrified me
I had two options. Option A: build on top of broken infrastructure, cross my fingers, and hope it wouldn't collapse under real volume. Option B: build the infrastructure myself.
I chose B. And it scared the hell out of me.
I shelved the securitization project — the entire reason I'd quit my career — and poured every real I had into building a core banking system from the ground up. Not a wrapper. Not a translation layer. Not an "abstraction" over someone else's COBOL. A real, honest-to-God core, built on the international standards that the rest of the industry was too lazy or too locked-in to adopt.
My former colleagues thought I'd lost my mind. Some days, looking at my dwindling bank account, I thought they were right.
Why Event Sourcing isn't optional — it's the law
Here's the dirty secret of traditional banking cores: they store the current state of an account (balance = $10,000) and throw away the history. How did it get to $10,000? A deposit? A reversal of a reversal? An adjustment someone made at 3 AM? The state doesn't tell you.
This is fine until a regulator shows up and asks: *"What happened to this account between March 3rd and March 7th?"* And you realize your system literally cannot answer the question.
At Revenu, we made a different choice. Every operation is an immutable event:
AccountOpened { id: acc_7x, type: checking, holder: "João Silva" }FundsDeposited { account: acc_7x, amount: 10000, source: pix_2x }TransferInitiated { from: acc_7x, to: acc_3y, amount: 5000 }
State is derived. Never stored directly. You want the balance? Replay the events. You want the balance *at a specific point in time*? Replay up to that timestamp. You want to debug a production issue? Pull the event stream and step through it like a movie in slow motion.
Event Sourcing isn't a technical preference. In regulated finance, it's the only architecture that doesn't require you to lie to auditors.
ISO 20022: we refused to build a translation layer
Most platforms do something that sounds reasonable but is actually insane: they generate proprietary internal messages, then translate them to ISO 20022 at the edge when they need to talk to payment networks. This "translation layer" approach means every message passes through a lossy conversion. Data gets dropped. Semantics shift. Edge cases silently break.
We refused.
Revenu generates ISO 20022 messages *internally*. From the start. Before they touch any external system:
pacs.008— Customer Credit Transferpacs.002— Payment Status Reportcamt.053— Bank to Customer Statement- Plus 16 more. 19 native message types in total.
No translation. No conversion. No "close enough." Every message is standards-compliant from the nanosecond it's created.
Why does this matter? Because SWIFT has mandated ISO 20022 migration. Central banks worldwide are adopting it. If your core generates proprietary formats and translates at the edge, you're not building a product — you're building technical debt with a payment deadline.
BIAN: making Temenos understand your PIX payment
We didn't stop at messaging. We adopted BIAN (Banking Industry Architecture Network) for our entire service architecture. Every microservice maps to a BIAN Service Domain.
The practical consequence is wild: a Temenos instance running in London can natively understand a PIX payment originated in São Paulo. No middleware. No custom adapters. No integration project. The systems speak the same language because we chose to speak it from day one.
For global players — Stripe, PayPal, Adyen — entering Brazil, this eliminates months of custom integration work. They plug into Revenu and get ISO 20022-native access to PIX, TED, Boleto, and the entire Brazilian payment ecosystem through a single connection.
We call this the tropicalization layer: making emerging-market payment rails speak global banking standards natively.
FIBO: when your data model is a regulatory advantage
Our data model is built on FIBO — the Financial Industry Business Ontology. Every entity (accounts, instruments, parties, contracts) follows semantic definitions used by regulators worldwide.
A single PIX payment in Revenu generates — simultaneously, atomically — four outputs:
- A native
pacs.008ISO 20022 message for SPI - A double-entry ledger posting following FIBO ontology
- A Kafka event on an AsyncAPI-documented topic
- A BIAN-compliant service interaction
Four standards. One transaction. Zero translation.
Kafka: not a message bus — the source of truth
In most systems, Kafka is an afterthought. A message bus. A way to "decouple services." At Revenu, Kafka *is* the system. Every state change emits an event to a Kafka topic as the primary source of truth.
- Commands validate business rules and emit domain events
- Projections consume events and build read models (CQRS)
- Sagas orchestrate multi-step processes — transfers, settlements, KYC flows
- External consumers subscribe via AsyncAPI-documented streams
Any system — yours or ours — can tap into the financial event stream in real-time. No polling. No batch files. No "we'll reconcile overnight."
CQRS: because reads and writes have nothing in common
Event Sourcing pairs naturally with CQRS. Our write side validates invariants and emits events. Our read side consumes events and serves queries from denormalized projections in under 5ms.
Balance queries and transfer processing never compete for resources. You scale them independently. The read side can go down and come back up by replaying events — no data loss, no inconsistency.
14 DDD aggregates: the domain *is* the code
- Account Aggregate — balance, limits, status transitions, multi-currency
- Transfer Aggregate — payment lifecycle (initiated → processing → completed → settled)
- Ledger Aggregate — double-entry postings with regulatory mapping
- Compliance Aggregate — KYC/KYB state machine, PEP screening, risk scoring
Each aggregate enforces its own invariants. An account can't go negative unless overdraft is explicitly enabled. A transfer can't settle if the source is frozen. The domain model isn't a layer on top of the business logic. The domain model *is* the business logic.
Four years later: the numbers
- 16.7K TPS sustained throughput
- 19 native ISO 20022 message types — zero translation layers
- 100% BIAN mapped — every service is a BIAN domain
- FIBO ontology — semantic interoperability with Temenos, Finastra, Mambu
- Zero data inconsistencies — event replay matches state 100% of the time
- 14 DDD aggregates — accounts, payments, ledger, compliance, cards, billing, credit, risk
- ISO 27001 certified — security by design, not by checklist
What I'd tell you over a beer
Building banking infrastructure is the loneliest startup you can build. There's no viral loop. No Product Hunt launch. No users tweeting about your beautiful UI. There are regulators questioning your architecture at 2 AM and compliance audits that last weeks.
But then a fintech launches a credit product in four weeks on your APIs. A marketplace routes $2.4M monthly through your split engine. A developer opens your docs and says: *"This is the first banking API that just works."*
And you know — you know in your bones — that the bet was right.
I delayed my securitization dream by four years. But I built something I couldn't find anywhere on the planet: a core banking system that remembers everything, speaks every standard, and doesn't ask you to compromise.
The old banking world stores state and hopes for the best.
We store truth.