Skip to content

ADR 0002: The backend is a headless library bundled into the browser ​

  • Status: accepted
  • Date: 2025-12 (library set up), layering since 2026-01-19, IndexedDB storage since 2026-02-06
  • Evidence: backend commits 43b5cfc (initial library), c23567c (implementations moved to the application layer, domain kept pure), e097e47 (IndexedDB persistence services); app/sidedoc-backend/package.json ("Headless library for browser-based document processing"); scripts/sync-dcat-mappings.mjs ("the backend runs in the browser and cannot read files at runtime")

Context ​

SideDoc needs a real document model: schema traversal, normalization of old documents, validation, and exports to HTML, PDF and DCAT-AP. It also has to be easy to host for the institutions that use it, and datasheets may describe data that is not yet public. Running an application server per deployment adds hosting cost, operations work and a place where user data would be stored.

Decision ​

  • Put all of that logic in a TypeScript library, @sidedoc/backend, with no UI and no server. It is called "backend" because it is the layer behind the UI, not because it runs on a server.
  • Build it with tsup to ESM and CJS with type declarations. The Vue app imports it and Vite bundles it, so it runs in the user's browser.
  • Store the working copy in IndexedDB. Remote storage is optional and goes directly from the browser to storage the user controls (Google Drive, S3, local folder).
  • Keep a layered structure: domain (contracts, entities, pure functions), application (services, adapters, transformers, persistence), infrastructure (IndexedDB).
  • Data that the library needs (vocabulary index, mapping files) is imported as JSON at build time.

Consequences ​

  • A deployment is static files. Hosting is cheap and the attack surface is small (ADR 0003).
  • No user data reaches a SideDoc server. The flip side: the workspace lives in one browser profile, and clearing site data loses it unless the user exported or used a storage provider.
  • Everything in the bundle is public, including any build-time configuration. Secrets must never be baked in.
  • The library has no Node-only dependencies (no fs, no process at run time). Mapping and vocabulary changes need a backend rebuild.
  • The library can be tested in isolation with vitest and in-memory services, and can be reused by other front ends, for example a desktop shell.