As a developer, I default to building things — but not every problem needs a custom app. My client work was scattered across a spreadsheet for invoices, a doc for project notes, and a notes app for task lists. None of it talked to each other. Every status update meant updating three places by hand.
The fix wasn't code. It was applying actual database thinking — foreign keys, relations, one-to-many — to a tool I'd been using like a glorified to-do list: Notion.
The problem with flat lists
Most people use Notion databases as flat tables. A "Clients" list. A separate "Projects" list. No connection between them. That's the Notion equivalent of denormalized data — duplicate info everywhere, no single source of truth.
The fix is the same one you'd reach for in Postgres: relations.
Modeling it like a schema
I mapped out four entities, the same way I'd sketch a schema before writing migrations:
Clients (1) ──< Projects (many)
Projects (1) ──< Tasks (many)
Clients (1) ──< Invoices (many)
Projects (1) ──< Invoices (many)
In Notion, this becomes relation properties — each database gets a field that points to rows in another database. Set it up as a two-way relation, and opening a client automatically shows every project, task, and invoice tied to them. No manual lookups, no copy-pasting a client name into three different sheets.
The four databases
Clients — contact info, status (Active/Paused/Past), industry. The "root" entity everything else relates back to.
Projects — relates to Clients. Adds status, deadline, budget, priority. This is where a Kanban view (grouped by Status) actually earns its keep — dragging a card from "In Progress" to "Review" feels like a real workflow, not just checking a box.
Tasks — relates to Projects (which relates to Clients, so you get the chain for free). Due dates, priority, assignee.
Invoices & Payments — relates to both Clients and Projects. This was the one I initially got wrong — I first related it only to Projects, then realized I wanted to filter "everything owed by Client X" without going through the project layer. Two relations, no downside.
Views matter as much as the schema
Once the relations exist, the same underlying data supports wildly different views:
Table — the full spreadsheet-style view, for bulk editing
Board (Kanban) grouped by Status — for daily workflow
Calendar grouped by Deadline — for "what's due this week"
This is the part that took me longest to appreciate: the data model is the hard part. Once it's right, views are nearly free.
Where this ended up
I turned the finished structure into a template — four connected databases, pre-filled with sample data so the relations are visible immediately instead of staring at empty tables. If you want the ready-built version instead of wiring up the relations yourself, I packaged it here: Client & Project Tracker — Notion Template.
Either way — if you're a freelancer or small agency juggling client info across too many tools, the fix probably isn't a new app. It's modeling the relations you already have data for.
Top comments (4)
The Invoices-relates-to-both-Clients-and-Projects detail is the part most people get wrong, so it's good you called it out. Going through the project layer to answer "what does Client X owe me" breaks the moment you have retainer work or a deposit that isn't tied to a single project.
One thing that saved me after building something similar: a rollup on Clients for "days since last touch," fed by the latest date across related Tasks/Invoices. Relations tell you what's connected, but they don't tell you what's gone quiet — and quiet clients are where freelance revenue actually leaks.
The other lesson I'd add to "the data model is the hard part": the schema handles storage, but nothing in it forces you to look. A good structure plus no weekly review habit still ends up as four stale databases instead of three stale tools. The Calendar view grouped by Deadline is probably your best hook for that — make it the page you open Monday morning rather than one view among many.
ngl this comment is better than the post
you're totally right about the retainer/deposit thing, invoices
tying to client only through project breaks immediately once it's
not project-shaped work. fixing that
the "days since last touch" rollup never even occurred to me. i was
so deep in "can i model the relations" that i didn't think about what
the data should actively surface. relations show what's connected,
not what's gone quiet - stealing that line lol
and yeah, calendar view just being "one view among others" instead of
the actual monday-morning anchor is fair. good structure with no
habit around it just becomes 4 stale databases instead of 3 stale
tools, you're right
appreciate you actually engaging instead of just dropping a "nice!"
Thanks for the thoughtful reply - glad the retainer/deposit and "days since last touch" bits landed. One extra thing that made the biggest difference for me: don't leave that rollup as a column people have to scan for. Turn it into a filtered saved view - filter "days since last touch > 10", sort descending - and name it something blunt like "Quiet clients". Then make that view, not the full database, the thing you actually open on Monday morning, right next to the calendar. It's the same data you already modeled, but it goes from something you could notice to something that's asking for a decision.
gonna set this up properly instead of just nodding along and not doing
it. appreciate you following through on the thread instead of just
dropping the idea once