Skip to content
Basil Francis Alajid
WatchLedger
Backend System

WatchLedger

2026Solo project
GoWatermillNATSgRPCPostgreSQL

Problem

Event-sourced portfolio tracker built on CQRS. Every trade and price update is appended as an immutable event; read models (holdings, performance) are projections rebuilt from the event log rather than mutated in place, giving full historical auditability.

Constraints

  • -Historical price data grows unbounded β€” queries against it must stay fast without manual archiving
  • -Read models must be derivable purely by replaying the event log, never mutated directly
  • -Financial calculations (IRR, CAGR, appreciation rate) must be correct against arbitrary buy/sell event sequences

Architecture

Commands are validated and appended to an append-only event log via Watermill message routing over NATS. Projections subscribe to the event stream and rebuild CQRS read models (current holdings, historical performance) on every new event. PostgreSQL stores both the event log and the projected read models, with table partitioning on historical price data to keep time-range queries fast as the log grows. gRPC exposes the command and query APIs.

Tech Choices & Why

Event sourcing + CQRS

An immutable event log gives full audit history for every trade and price change, and lets read models be rebuilt or corrected by replaying events.

Watermill

Go message-routing library providing command/event bus plumbing between the write side (commands) and the read side (projections) without a heavier framework.

PostgreSQL table partitioning

Historical price data is append-heavy and time-ordered β€” partitioning keeps range queries fast as the dataset grows.

Key Metrics

Architecture

CQRS + Event Sourcing

Stack

Go + NATS + Postgres

Data

Partitioned Time Series

Challenges & Solutions

Problem

Read models need to reflect every historical trade and price update without direct mutation, while staying fast to query.

Solution

CQRS projections are rebuilt from the append-only event log via Watermill-routed events; PostgreSQL table partitioning on historical price data keeps read-side queries fast.

Problem

Financial calculations like IRR and CAGR need to be correct for arbitrary sequences of buy/sell events, not just simple buy-and-hold.

Solution

IRR, CAGR, and appreciation-rate figures are derived directly from the replayed event stream rather than from a mutable running balance, so out-of-order corrections replay correctly.

Trade-offs

  • ~Event sourcing adds complexity versus a simple CRUD ledger β€” justified here by the audit-trail and replay requirements.
  • ~Rebuilding projections from the full event log is more expensive than a mutable table, offset by only replaying on schema changes or corrections.

Impact

Demonstrates a CQRS + event-sourcing architecture in Go: an append-only event log, replayable projections, and partitioned time-series storage for financial calculations (IRR, CAGR, appreciation rate) with full historical auditability.

What I'd Improve

  • +Add snapshotting to avoid full event-log replay for accounts with long histories
  • +Add multi-currency cost-basis tracking

This project was reviewed by Luka (Principal Research Consultant) and Nala (UI/UX Compliance Auditor). They had no comments, which we interpret as approval.

You found the bottom! 🎯