From Idea to Code: Low-Level Design of a SettleUp-Like App (.NET Core + React + SQL)
For content overview videos
https://www.youtube.com/@DotNetFullstackDev
If you’re a .NET developer, this blog is the missing bridge between:
system-design interviews
and real production code
We’ll design a SettleUp-like expense-splitting app, end to end:
backend in .NET Core
frontend in React
database in SQL
And more importantly, we’ll explain why this stack makes sense — not because it’s popular, but because it fits the problem.
Why this tech stack (before we design anything)
Let’s answer the “why” upfront, because architecture always starts with constraints.
Why .NET Core for backend?
Because this problem needs:
strong domain modeling
clear separation of concerns
predictable performance
long-term maintainability
.NET Core gives us:
clean layering (Domain, Application, Infrastructure)
excellent async handling (important for APIs)
strong typing for financial logic
mature ecosystem for auth, logging, validation
This is business logic-heavy software, not a script.
.NET shines here.
Why React for frontend?
SettleUp-style apps are:
highly interactive
state-driven
real-time feeling (balances update instantly)
UI-heavy
React fits because:
UI is just a function of state
balance recalculations reflect immediately
components map cleanly to domain concepts
ecosystem supports charts, lists, notifications easily
This isn’t a static website.
It’s a living financial dashboard.
Why SQL database?
This is the most important choice.
Expense sharing is financial data.
That means:
correctness > flexibility
consistency > speed hacks
auditability > schemaless freedom
SQL gives us:
transactions
referential integrity
constraints
predictable queries
easy reconciliation
Ledger-style systems and relational databases are a natural fit.
Big picture: how the system is split (low-level view)
We split the system into three responsibilities:
Frontend (React)
collects user intent
displays derived data (balances, settlements)
Backend (.NET Core API)
enforces business rules
computes balances
guarantees correctness
Database (SQL)
stores immutable financial history
never lies
Frontend never calculates money.
Backend never trusts frontend numbers.
Database never stores “derived” values blindly.
This separation saves you from nightmares later.
Backend low-level design (.NET Core)
Let’s go layer by layer — the way you’d actually structure the solution.
1️⃣ API Layer (Controllers / Endpoints)
This layer does one job only:
accept requests
validate shape
call application services
return responses
It must not:
calculate balances
apply business rules
talk directly to EF entities
Example responsibilities:
POST /groupsPOST /expensesGET /groups/{id}/balancesPOST /settlements
Controllers should feel boring.
That’s a good sign.
2️⃣ Application Layer (Use-Cases)
This is where intent becomes action.
Each use-case represents:
a business action
a user intention
Examples:
CreateExpense
AddGroupMember
RecordSettlement
GetGroupBalances
GetSimplifiedSettlements
Each use-case:
validates business rules
coordinates domain objects
controls transactions
Think of this layer as:
“The orchestrator of business workflows”
3️⃣ Domain Layer (the heart of correctness)
This is the most important part of SettleUp.
Here you model:
Expense
Split
Settlement
Balance calculation rules
Key design principle:
Domain objects protect invariants.
For example:
Total splits must equal total expense
Settlement amount must be positive
A user cannot owe themselves
Currency mismatch must be explicit
If these rules leak outside the domain, bugs creep in silently.
4️⃣ Balance calculation service (core logic)
This deserves special attention.
You do not store balances permanently.
Instead:
fetch all ledger entries (expenses + settlements)
compute net balances per user
simplify debts if required
This logic must be:
deterministic
testable
independent of database concerns
This is where interviews are won and production bugs are avoided.
5️⃣ Infrastructure Layer (EF Core + SQL)
This layer:
persists domain entities
maps objects to tables
manages transactions
Important design choice:
expenses and settlements are append-only
updates create new records or mark old ones as inactive
Why?
Because financial history must be explainable.
Never “rewrite” money.
Database low-level design (SQL mindset)
The database represents truth, not convenience.
Ledger-first storage
Instead of:
storing “A owes B ₹500”
We store:
who paid
who owed
who settled
when
how much
Balances are derived views, not stored facts.
This enables:
recomputation
audits
debugging disputes
future rule changes
Transactions everywhere
Any operation that:
creates an expense
records a settlement
must be wrapped in a transaction.
If one insert fails, nothing should persist.
Money hates partial success.
Constraints > Code checks
Use SQL constraints for:
foreign keys
non-negative amounts
required relationships
This protects you even if:
someone bypasses the API
a bug slips into code
Frontend low-level design (React)
Now let’s move to the UI — where things feel complex.
State-driven UI (not imperative UI)
In React, the UI is:
a projection of state
That means:
no manual DOM updates
no recalculations in UI
backend sends truth
frontend renders truth
Example:
user adds expense
API returns updated balances
React re-renders automatically
Frontend should never “guess” balances.
Component structure (domain-aligned)
Good React apps mirror backend concepts:
GroupList
GroupDetails
ExpenseList
AddExpenseForm
BalanceSummary
SettlementSuggestions
When frontend names match backend concepts:
bugs reduce
onboarding is faster
communication improves
API contract discipline
Frontend talks to backend via:
DTOs
well-defined response models
Never expose EF entities directly.
Frontend should not depend on:
internal IDs
database structure
backend refactoring
This decoupling is what allows change without pain.
Why this LLD scales (even if the app grows)
This design supports:
thousands of expenses
many groups
multiple currencies
future features (notifications, analytics)
Because:
ledger is immutable
business logic is centralized
UI is stateless relative to calculations
derived data is recalculated, not guessed
You don’t paint yourself into a corner.
Common mistakes this design avoids
Let’s be honest — these are mistakes many devs make:
❌ calculating balances in frontend
❌ storing “who owes whom” directly
❌ mixing EF entities in controllers
❌ updating past financial records
❌ trusting UI calculations
This design deliberately avoids all of them.
Keep the Momentum Going — Support the Journey
If this post helped you level up or added value to your day, feel free to fuel the next one — Buy Me a Coffee powers deeper breakdowns, real-world examples, and crisp technical storytelling.
Final takeaway (the one to remember)
If you remember only one thing from this blog, remember this:
SettleUp is not a CRUD app.
It’s a ledger-driven financial system with a friendly UI.
When you design it like a ledger:
correctness becomes natural
edge cases become manageable
scaling becomes predictable
interviews become easier
production bugs reduce drastically



Thanks for writing this, it clarifies a lot; understanding *why* a particulr stack fits the domain problem so perfectly is always a source of wonder and deep insight.