Skip to content
Finance Archive

Case study · Finance Archive

A finance library that teaches judgement, not just definitions.

Three student failures led to one content model, one finance engine, and one read boundary. The result is a platform where every article follows the same method — and where a configuration bug once masqueraded as a broken database.

Open the archiveRead the method
Articles
25
Concepts
31
Formulas
76
Seed documents
303
Role
Product, content architecture, front end, data layer
Stack
Next.js · TypeScript · MongoDB
Scope
Archive · Lab · Practice · Collaboration
Status
Live demo · 303 seeded records

This is a working record, compiled from the repository and its live data. Counts are read at request time from the same boundary the application uses, and every benchmark below is asserted by the test suite. No adoption figures or projected outcomes have been invented.

01 · The problem

Three failures that compound.

Most finance resources solve one of these and leave the other two untouched. The archive was designed around the connection between them.

01

Piles, not paths

Notes and formulas are collected in isolation. There is no ordering, no prerequisite, and no signal for what to study next.

The response

Tracks, prerequisites, curricula, and typed relations

02

Reading without checking

A topic can be read without ever testing whether the idea survived contact with a question. Recognition gets mistaken for understanding.

The response

Practice items, self-checks, mastery bands, and exams

03

Theory without use

Even an understood formula is rarely applied to a decision, so judgement — the actual goal — never forms.

The response

A calculator, a worked case, and reflection in every article

The constraint that shaped everything else was coherence: a new topic should inherit the method automatically, and a number shown in two places should never be produced twice.

02 · One method

Learn. Calculate. Apply.

The method is not a page template. It is the shape of a note record, which is why every article supports the same three acts without bespoke wiring.

01

Learn

The idea, the formula, the assumptions, and the points that matter. Written to be read once and revisited later.

sections[]
02

Calculate

A live tool wired to the same record. Change an assumption and watch the result move, rather than trusting a screenshot.

calculate.kind
03

Apply

A decision case and self-check questions. The reader has to choose, not merely recognise, and receives an explanation either way.

apply.caseStudy · apply.questions[]

Why this matters for maintenance

An article becomes interactive by naming a calculator. The renderer reads the kind, loads the matching engine surface, and supplies the same record the reader is already seeing. There is no per-article screen to build, no parallel copy of the formula, and no place for the prose and the tool to drift apart.

Authoring path

  1. 1. Write one typed note record.
  2. 2. Name a calculator kind.
  3. 3. Add a case and self-check questions.
  4. 4. Validate, then publish.
The method survives because it is enforced in data, not remembered by whoever builds the next page.

03 · Architecture

A short stack with a fixed direction.

Dependencies point one way. The interface asks for content, the application boundary answers, and the finance engine owns every calculation.

Dependency direction

UI → Feature → Application → Domain → Data
  1. 01

    UI

    src/app · src/components

    Server-rendered pages and focused client components. Renders records; never becomes the source of a number.

  2. 02

    Feature

    src/features

    Practice, collections, collaboration, Lab workspaces. Owns interaction state and the shape of a workflow.

  3. 03

    Application

    src/lib

    The read boundary. Pages ask for notes, formulas, and practice items; they never reach into data or MongoDB directly.

  4. 04

    Domain

    src/domain

    A React-free finance engine with its own tests: statements, valuation, risk, and formula experiments.

  5. 05

    Data

    src/data · src/server

    Typed seed records, the MongoDB repository, and the seed plan that keeps both representations in step.

The interface never calculates

Arithmetic lives in the finance engine. Charts, cards, and pages display its output, so a result cannot drift between two screens.

One read boundary

Every page reads through the same async functions. Switching from seed data to MongoDB changes the answer source, not the component.

AI explains, never computes

Generated guidance is bounded to plain-language explanation. Figures always come from the deterministic engine.

Writes are explicit

Reads fall back to seed data; writes require a database and say so. A missing connection degrades the feature instead of faking success.

The read boundary in practice

A page calls getNotes() or getFormulas(). With a connection string the repository answers from MongoDB; without one it answers from the typed seed records. The component tree does not know which happened, so the demo, the test suite, and the deployed app run the same UI code.

04 · Content system

Live coverage, read from the source.

These are the current records at request time, not a launch-day claim. The same numbers feed the archive, the knowledge map, practice, and the seed plan.

Studio and seed parity

Content can be authored in the browser studio or as a typed seed file. Both paths produce the same validated document, so seeding MongoDB does not create a second content model.

A knowledge layer above notes

Concepts, typed relations, domains, and curricula give the archive memory. Mastery and recommendations read from that graph rather than inferring structure from page titles.

A second reading mode

The Ledger applies the same pipeline to finance essays — capital raising, hedging, the CFO role, the accounting profession — because an argument is read differently from a technique. It has its own record type, but shares the read boundary, repository, and seed plan.

Read the Ledger

05 · Design

Premium by restraint, not decoration.

One semantic token set controls the navy and gold palette, type, spacing, radii, and motion. Every page composes the same primitives instead of introducing a new visual vocabulary.

Mobile first

The header collapses to a menu, tables wrap instead of overflowing, charts scroll inside their own frame, and every interactive target is at least 44px.

Laptop deliberate

Six top-level destinations keep the header readable. Dropdowns hold the deeper routes, while long reads gain a sticky contents rail and a two-column rhythm.

Motion with restraint

Reveals, lifts, and the floating ring motif are short and opt-in. All of it is disabled under prefers-reduced-motion, and content is visible before hydration.

Presentation rule

Interior pages should feel like they belong to the same building — not like a plain white room beside a designed entrance.

The shared gradient page hero, concentric rings, masked grid, and gold rule now open the Archive, Lab, Practice, Knowledge, and this case study. The design does not depend on a screenshot to explain itself.

06 · Debugging

The database bug that was not a database bug.

The most instructive failure in the build looked like configuration. The app had the right connection string, the right database, and a healthy-looking read path — and still refused every write.

  1. 1

    The symptom

    /api/health reported the database as configured, but the driver as missing. Seed and read routes still worked, so the site looked healthy while every write returned 503.

  2. 2

    The misleading surface

    The read boundary did exactly what it promised: when the database was unavailable, it served typed seed data. That resilience hid the real fault. A healthy-looking page was not evidence of a healthy connection.

  3. 3

    The bundler boundary

    The driver specifier had been assembled at runtime to keep it out of the bundle. Turbopack could not analyse it, so it substituted a stub that threw “Cannot find module as expression is too dynamic.” The surrounding catch then folded that build-time problem into the same quiet fallback used for a missing database.

Before · unanalysable specifier

const driver = await import(
  ["mongo", "db"].join("")
);

After · literal, externalised specifier

const driver = (await import("mongodb")) as
  MongoDriverShape;

What changed

  • The import is now literal, and mongodb is declared as a server external package.
  • Health reports driverInstalled separately from connected, so the two failures cannot collapse into one.
  • The seed route returns the real reason and the connection state, instead of a generic refusal.
  • A driver failure is no longer cached, so installing the package can recover without restarting the process.
A graceful fallback is valuable — but only if it also tells you when it is being used.

07 · Results

Verified, not projected.

The build is checked at three levels: static analysis, engine behaviour, and a live Atlas connection. These are the recorded results.

0

TypeScript errors

strict compile

0

Lint warnings

full codebase

82

Automated tests

across 12 files

303

Seed documents

across 13 collections

Hand-checked engine benchmarks

  • Bond price৳871.65

    Discounted cash flows at the stated yield

  • Gordon value৳484.57

    Dividend growth model with stable assumptions

  • CAPM12.9%

    Risk-free rate plus beta times the premium

  • WACC10.89%

    Weighted cost of debt and equity

  • Project IRR24.92%

    Rate at which project NPV reaches zero

Benchmarks are recorded in VALIDATION.md and asserted in tests/formulas.test.mjs. The live connection reports configured: true, connected: true, and source: "database".

08 · Tradeoffs

What this build deliberately does not claim.

A case study is less useful if it only lists strengths. These are the boundaries a reader should know before treating the platform as more than it is.

01

No accounts yet

Progress, saved notes, and collections persist in the browser. That keeps the demo honest and frictionless, but it is not yet a multi-device identity.

02

Marking stays local

Answers are checked in the client against the same typed questions. A production cohort would move results behind authenticated writes.

03

Money is illustrative

The archive teaches mechanics with sample figures. It is not a market feed, a valuation opinion, or financial advice.

04

Two document shapes

Articles and Ledger essays deliberately use different records. Reading an argument is a different act from practising a technique, and forcing one shape would weaken both.

The demo-first decision is the largest tradeoff. It made the platform immediately usable and testable, but it also means a browser can look complete while persistence is local. The health endpoint and this page state the distinction plainly rather than hiding it behind an account wall.

09 · Lessons

What made the platform maintainable.

These are engineering lessons that came from building the archive, not generic advice added afterwards.

  1. 01

    Enforce the method in the data model

    A three-stage record makes every new article consistent without a per-article layout. The interface follows the content instead of memorising it.

  2. 02

    Keep computation out of the view

    A React-free engine is testable, reusable across the Archive and the Lab, and impossible to bypass with a convenience calculation in a component.

  3. 03

    A demo-first boundary compounds

    Seed fallback made the platform presentable and testable before a database existed. The same boundary then became the production read path.

  4. 04

    Responsive means the real devices

    Designing mobile and laptop deliberately beat chasing every breakpoint. The important layouts were proven where students actually read.

  5. 05

    Document the seams

    Written specs for the content model, finance engine, and data layer are what let a growing platform change without losing its shape.

The through-line

Every lesson points back to the same idea: make the right thing the easy thing. A consistent record shape, a single calculation owner, a single read boundary, and written documentation reduce the number of decisions the next change has to get right.

10 · What comes next

The honest edge of the build.

The front end and data layer are complete enough to demonstrate the whole product. The next phase is identity, persistence, and turning the knowledge graph into better discovery.

The current demo stores progress, saved notes, and collections in the browser. The API already exposes the write surfaces and refuses them cleanly without a database, so adding accounts is an extension of the architecture rather than a rewrite of the interface. The same is true of discussion persistence and concept-aware search.

ZH

Creator & maintainer

Zisanur Haque

About this build

Designs and builds the archive — the content model, the finance engine, and the interface. The goal was not to add more finance pages. It was to make the relationship between explanation, calculation, and judgement durable enough that new topics could be added without weakening the experience.

The full documentation set — architecture, content model, finance engine, design system, API, seeding, and validation — is indexed on the about page. The repository also carries docs/CASE-STUDY.md and VALIDATION.md.

Study the method

Read an article. Change an assumption. Make a decision.

The archive is free to read, practise, and download. Start with the foundations, follow a track, or open the Lab and test an idea against a real set of statements.