Marginalia

M
Marginalia

Library management system

A library system prototype to practice modelling a real domain end to end — cataloguing, patrons, and circulation. It has a staff console for the desk and a public catalogue (OPAC) for patrons.

Features

Staff

Staff console

The operational side: catalogue records, the circulation desk, and the hold shelf.

  • Circulation desk — check out, return, and renew; load a patron; work the hold shelf.
  • Catalog — the Work → Manifestation → Item tree, with availability derived from loans and holds.
  • Records — register and edit patrons; move items through repair, lost, and withdrawn.
Staff circulation desk with a patron loaded
The desk, with a patron loaded — their loans, the counts, and the hold shelf.
Patron

Public catalogue (OPAC)

What a patron sees: search the catalogue, place holds, and check their own account.

  • Browse — search by title, author, or ISBN; filter by material type.
  • Holds — reserve an edition and see your place in the queue.
  • My library — current loans with due dates, and holds with their pickup dates.
Patron-facing OPAC browse screen
The public catalogue, with availability shown per edition.

More screens

Staff catalog screen
Staff · Catalog — the Work → Manifestation → Item tree, and cataloguing a new title.
Patron My library screen
Patron · My library — one loan that's overdue, and a hold that's ready to pick up.
OPAC sign-in modal
Patron · sign-in — there's no password; the card number is the identity, like a real library.
OPAC on a phone
The OPAC on a narrow screen.

Domain

The catalogue follows the FRBR model.

Cataloguing

Work → Manifestation → Item

A Work has editions (Manifestations), and each edition has physical copies (Items). A copy's availability isn't stored — it's worked out from its state, its loans, and its holds.

Patrons

Cards, categories, and policy

A patron has a category and a card that can expire; a suspended or expired card can't borrow. Loan periods and limits come from a policy keyed by category and material type, not hard-coded into the entities.

Circulation

Loans and holds

Check out, return, and renew, with due dates. Holds queue per edition, go to the shelf when a copy comes back, and expire if nobody picks them up — swept on a schedule.

Demos

Staff workflow demo
Staff — loading a patron, checking out a copy, the catalog tree, a copy into repair, and a return.
OPAC demo
Patron — searching the catalogue, placing a hold, and finding it in My library.

Clean Architecture

The code follows Clean Architecture — dependencies point inward. The domain and application layers import no web or database library, and a test enforces that.

flowchart LR
  SPA["React SPA (staff + OPAC)"] -->|HTTP, same origin| IF["Interface: routers, schemas"]
  IF --> APP["Application: use cases"]
  APP --> DOM["Domain: entities, policy, services"]
  APP --> PORTS["Repository ports"]
  INFRA["Infrastructure: SQLAlchemy, Alembic"] -. implements .-> PORTS
  INFRA --> DB[("SQLite / Postgres")]
  • DomainEntities, the loan policy, and domain services. Plain Python, no framework.
  • ApplicationUse cases over repository ports.
  • InterfaceFastAPI routers, Pydantic schemas, and the React SPA.
  • InfrastructureSQLAlchemy repositories and Alembic migrations.

How it's built

  • Spec first — every requirement in SPEC.md is traced to the test that verifies it.
  • Tests — unit and integration in pytest, end-to-end in Playwright.
  • Decisions written down — ADRs for the bibliographic model, the loan aggregate, and so on.
  • CI on every PR — the test suites, a Storybook build, and ruff.
  • Migrations — Alembic owns the schema; the app migrates on startup.