Day 3: Primitives Before Features

architecture dexie data-model

Today we built the data foundation for Accelerate Finance. Not features—primitives. The atoms that will compose into every use case we haven't imagined yet.

Why Dexie?

We chose Dexie.js as our local database layer. It's a wrapper around IndexedDB that gives us:

  • Reactive queries — liveQuery() returns observables that update when data changes
  • Schema versioning — Built-in migrations for evolving data models
  • Compound indexes — Complex queries without performance penalty
  • Transactions — ACID guarantees for data integrity

The Six Primitives

After much deliberation, we settled on six core primitives. Each is designed to be minimal but complete:

1. Workspace

The container for everything else. One workspace = one financial context (personal, business, project).

id, name, currency, metadata

2. Account

Bank accounts, credit cards, cash, investments. Where money lives.

id, name, type, initialBalance, currentBalance

3. Transaction

Income, expenses, transfers. The atomic unit of financial activity.

id, accountId, type, amount, date, description

4. Category

Classification for transactions. Hierarchical (parent/child) for flexibility.

id, name, type, parentId, budget

5. Rule

Automation primitives. Auto-categorize, split, rename transactions based on conditions.

id, name, conditions, actions, priority

6. View

Saved perspectives on data. Filters, sorts, columns. Future: shareable via links.

id, name, type, filters, sort, columns

Design Decisions

Soft Delete Everywhere

Every entity has a deletedAt tombstone. Nothing is truly deleted—it's marked. This enables future sync, undo, and audit trails.

Amounts in Cents

All monetary values stored as integers (cents/smallest unit). No floating-point precision issues. $100.50 = 10050.

Metadata Bags

Every entity has a typed metadata object for extensibility. AI can reason over this. Future features can add fields without migrations.

Repository Pattern

Clean abstraction over Dexie. Components never touch the database directly. Makes testing easy, makes swapping storage possible.

The Repository Layer

Each primitive gets a repository with consistent methods:

create(data) → entity
getById(id) → entity | undefined
getByWorkspace(workspaceId) → entity[]
liveByWorkspace(workspaceId) → Observable
update(id, changes) → void
delete(id) → void  // soft delete

What We Built Today

  • Schema module — TypeScript interfaces for all six primitives
  • Database module — Dexie setup with indexes and versioning
  • Repository layer — CRUD + live queries for each entity
  • Demo seeding — Sample workspace with accounts, categories, and transactions
  • /app route — Workspace creation and ledger view

The Philosophy

We resist the urge to build features. Features are compositions of primitives. If we get the primitives right, features emerge naturally.

Budgeting? Categories with budget metadata + transaction filtering.
Reports? Views with date filters + aggregation.
Automation? Rules matching transactions to actions.
Sharing? Views that serialize to shareable links.

Primitives before features. Always.

With the data layer in place, we can now build anything. The foundation is solid. Tomorrow we'll work on making the ledger view actually useful.

— The Accelerate Finance Team