Files
mangaBookmark/docs/adr/0004-wire-format-does-not-mirror-the-schema.md
T
sulthan 7a0c190ebe docs: record the domain model and the Postgres/OAuth ADRs
Written while scoping #18. CONTEXT.md pins the ubiquitous language
(Series, Reader, Bookmark, Progress, Latest Chapter, Poll) that the
schema split and the ordering rule are argued in; the four ADRs record
the decisions that follow from it, starting with Postgres over SQLite.

Refs #18
2026-08-08 06:43:38 +07:00

1.7 KiB

The wire format stays flat and deliberately does not mirror the schema

Status: accepted

Storage splits a tracked series across two tables (ADR-0003), but GET /bookmarks and PUT /bookmarks/{key} keep emitting and accepting one flat JSON object with title, cover, last_chapter and latest_chapter as siblings — exactly the shape they had when there was one table. The server joins on the way out and decomposes on the way in.

Why a future reader will find this surprising

The obvious move after splitting a table is to nest the JSON to match. Don't "fix" this.

A nested payload would have broken every installed userscript instantly. Scripts read b.title directly; moving it to b.series.title yields undefined — no error, just blank rows and a New Chapter signal that silently reports nothing forever. Because Violentmonkey updates roughly once a day per device, the migration relies on old scripts continuing to work during a 14-day grace window. A nested format and that grace window are mutually exclusive.

It is also the better contract independently of compatibility. A client rendering one row needs the title and the reading position together; nesting exports the re-stitching to every browser to mirror a decision about disk layout it should not know about. Keeping them separate lets storage change again later without a client release — which is the whole reason this ADR is worth the paragraph.

Consequence

The flat shape is a contract, not an implementation detail. Changing the storage schema must not change it. It follows the rule already in force for updated_at: the server owns the truth and returns the row as stored, and clients adopt the response rather than their own payload.