Problem statement
Design the object model and core APIs for a personal note-taking application that runs on one user's device and keeps their notes organised, searchable, and revertible.
Operating context. A single user owns thousands of notes. Notes live inside notebooks that can nest — a notebook may contain notes and other notebooks. Each note has a title, a body of block content (paragraphs, checklists, code blocks), and free-form tags. The user edits notes constantly; every save appends a new version so an accidental overwrite can be rolled back. Full-text and tag search must return matches quickly. Everything is in-process and offline — no server round-trip.
Out of scope. Cross-device sync and merge-conflict resolution, real-time collaborative editing, the rendering / UI layer, end-to-end encryption at rest, and attachment / blob storage (assume note bodies are text blocks only for v1).
What to produce. The class hierarchy (Note, Notebook, Tag, Block, VersionHistory, the search index, and the services binding them), the public API each class exposes, and the states a note moves through. Be explicit about: how the notebook tree is modelled so a note and a sub-notebook share one traversal, how the search index stays consistent as notes are created / edited / deleted / moved, and how version history is captured without cloning the whole note body on every save.
Functional requirements
- Create, rename, move, and delete notes and notebooks, where a notebook may hold both notes and other notebooks.
- Save an edit as a new immutable version and let the user restore any earlier version of a note.
- Attach and remove free-form tags on a note and list every note carrying a given tag.
- Run a full-text query over note titles and bodies and return matching notes ranked by relevance.
- Move a note between notebooks without breaking its tags, version history, or search entries.
Non-functional requirements
- Tag and full-text lookups run in near-constant time via an inverted index, not a linear scan of all notes.
- The index and version store sit behind interfaces so an in-memory implementation swaps for a disk-backed one without touching note logic.
- Version capture stores deltas or shared references rather than deep-copying the entire note body on each save.
- The domain model is unit-testable with an injected clock and no real storage backend.
- Adding a new block type (e.g. a table block) is a localised change that does not modify Note or Notebook.
- A read of a note during a background re-index never observes a half-updated index.
Topics
- System Design LLD
- Oop Solid
- Patterns Composite
- Data Indexing
- Patterns Memento