Skip to content
Jens Gross edited this page Sep 8, 2026 · 2 revisions

Courtside is a court booking system for sports clubs. Each club runs its own instance against its own database, so there is no shared installation and no tenant column anywhere in the schema. It is one Spring Boot application, one PostgreSQL database, and a React PWA that talks to the same public REST API a third party would use.

This wiki is the introduction. It is not the specification and not the ruleset, and where it disagrees with a file in the repository, the file is right. What it does is get somebody from "I cloned this" to "I know where to look".

Where truth lives

What you want to know Where it is written
What the product is, and why the architecture is shaped this way docs/design.md
What already exists, as opposed to what is designed docs/design.md, section 0
The rules for changing the code CLAUDE.md
How to set up, verify and open a pull request CONTRIBUTING.md
What every table holds docs/data-model.md
Every endpoint, as the source of truth rather than a description of one src/main/resources/api/openapi.yaml
How an instance is deployed and configured deploy/README.md
What a member or a board is told the documentation site in site/

One warning about the specification, because it costs people time: docs/design.md is written in the present tense as a target. Most of it describes the design and not the build. Section 0 and the inline built / designed markers are the parts that describe what actually exists, and they are the parts the code is obliged to keep true.

The one page here that carries substance of its own is Architecture: the modules, and why they may not call each other freely.

Where to start reading

Four stops, in this order, are enough to orient yourself in the tree.

  1. CourtsideApplication is the only class that sits outside a module. Its @Modulithic annotation names the two modules every other module may use.
  2. A package-info.java beside a module package — booking's is the fullest. That file is the boundary: it lists the modules its own module may depend on, and the build fails on anything else. The two shared modules are the exception and look empty: shared carries no annotation, and api has no such file at all, because the whole package is generated.
  3. booking next to booking/internal, booking/series and booking/web. The base package holds what other modules may touch; every sub-package is closed to them, a controller as much as a repository. Nothing enforces this by convention, a test enforces it.
  4. One request end to end: an operation in openapi.yaml, the interface the generator writes from it, BookingController implementing that interface, BookingService behind it, and the exclusion constraint in V3__booking.sql that has the final say on whether the booking exists.

Running it

node tools/courtside.mjs dev starts the application against a local database, and node tools/courtside.mjs check is the verification a branch has to pass. Docker has to be running for either, because the tests use Testcontainers. The rest of the setup, including the environment variables that create the first administrator, is in README.md and CONTRIBUTING.md.

Courtside is AGPL-3.0. What that means for a club that runs it, and for anyone who changes it, is in LICENSE and in the contributing guide.

Clone this wiki locally