0) Problem Restatement
Amazon asked: design an e-book reader service with APIs, a database schema and an architecture. A user buys books, reads offline, and uses several devices (phone, tablet, e-reader) for the same book. The system must sync reading position, bookmarks, highlights and notes across devices, even when devices were offline and made changes at the same time.
1) Requirements
- Library of owned books, and download of book files (DRM-protected).
- Read fully offline.
- Sync position ("page 142 on your phone, jump there?"), bookmarks, highlights and notes.
- Resolve conflicts from offline edits sensibly.
2) Architecture
Architecture Diagram
flowchart LR
D1["Phone"] -->|"sync changes"| SYNC["Sync Service"]
D2["E-reader"] --> SYNC
SYNC --> DB[("Reading state + annotations DB")]
D1 -->|"download"| CDN["CDN - encrypted book files"]
LIC["License / DRM service"] --> D1
STORE["Store / purchases"] --> LIB[("Library - entitlements")]
SYNC --> LIB
SYNC -->|"push: new position"| D23) Data Model
books: book_id, title, authors, format, file_versions
library: user_id, book_id, acquired_at, license_status
positions: user_id, book_id, device_id, location (e.g. EPUB CFI / percent), updated_at
annotations: annotation_id (UUID from device), user_id, book_id, type (bookmark|highlight|note),
range_start, range_end, text, color, updated_at, deleted (tombstone), version
Locations use a stable format that doesn't depend on font size (e.g., an EPUB CFI or a character offset), since "page 142" differs per device.
4) Offline-First Sync
- The device keeps everything locally and records changes in a local log (position updates, new or edited or deleted annotations) with timestamps.
- When online, it sends
POST /sync { since_cursor, changes[] }. The server applies them and returns all changes since the cursor from other devices, plus a new cursor. - Conflict rules (keep them simple and explain them):
- Reading position: store each device's latest position. When opening a book, if another device is further ahead (or more recent), offer "Jump to page X?" (Kindle's "furthest page read" behavior). Don't move the user automatically.
- Annotations: each has a unique client-generated ID, so creations never conflict. Edits of the same note on two devices → last-write-wins by timestamp (or keep both versions as a conflict copy). Deletes are tombstones, so a stale device doesn't resurrect the note.
5) Downloads and DRM
- Book files are encrypted and served by a CDN. The device gets a license (a key bound to the device and account) from the license service. A device limit per book or account applies.
- Returning or refunding a book revokes the license at the next sync.
6) Wrap-Up
Store the library, per-device reading positions (in font-independent locations) and annotations (client-generated IDs, versions, tombstones) on the server. Devices work offline and sync by sending their local change log and pulling changes since a cursor, with simple conflict rules: offer a jump to the furthest or most recent position, last-write-wins for edited annotations, and tombstones for deletes. Deliver encrypted books through a CDN with device-bound licenses.