Files
werkator/docs/adrs/0004-2026-07-07.rewrite-architecture.md
T

3.8 KiB
Raw Blame History

Rewrite Architecture Decisions

Status:

  • proposed: 2026-07-07
  • accepted: 2026-07-07
  • rejected: -
  • superseded: partially on 2026-07-07 — the "no managed nginx/TLS" part is revised by ADR 0005 (opt-in managed nginx/TLS container for hosts without a reverse proxy)

Decision [accepted, nginx part revised by ADR 0005]: JSON-file persistence behind a repository interface, server-rendered UI with JSON polling, no managed nginx/TLS by default — deployment via systemd user unit behind the host's reverse proxy.

Context and Problem Statement

The rewrite of legacy/werkator (bash) as a Kotlin/Spring application (see docs/plan/) required several cross-cutting architecture decisions. They were proposed in docs/plan/README.md, validated step by step during implementation, and are summarized here as one record.

Technical Background

The legacy script kept all state in TSV/HTML files, patched its web UI with regex rewrites, and managed its own nginx+certbot Docker container. Its two structural defects — build status not observable during a build, and a web UI that could get stuck loading forever — had to be fixed by design, not by patching.

Considered Options

  • Database persistence (SQLite) vs. JSON files behind a repository interface
  • SPA frontend or server push (WebSocket/SSE) vs. server-rendered HTML with JSON polling
  • Managed nginx/Let's Encrypt container vs. documented deployment behind an existing reverse proxy

Persistence: JSON files behind BuildResultRepository

Build results are persisted as a JSON file under .git/werkator/, accessed only through the BuildResultRepository interface.

Advantages

  • No database dependency, no schema migrations; state stays inspectable with a text editor, like legacy.
  • The volume is tiny (retention prunes per branch), so file rewrites are cheap.
  • The interface keeps a later switch to SQLite possible without touching callers.

Disadvantages

  • No queries or transactions; concurrent writers must be serialized in-process.

Web UI: server-rendered Thymeleaf plus JSON polling

Pages render the full state server-side; one hand-written JavaScript file polls JSON endpoints and re-renders table bodies.

Advantages

  • No SPA framework and no frontend build pipeline.
  • Fixes the legacy stuck-spinner defect by design: every fetch has a timeout and an explicit error badge, and status transitions are event-driven and observable while a build runs.
  • Pages stay useful without JavaScript (initial render is complete).

Disadvantages

  • Updates are only as fresh as the polling interval; no server push.

Deployment: no managed nginx, systemd user unit instead

nginx/Let's Encrypt container management was not ported; init --systemd generates a user unit running java -jar werkator.jar server, and docs/deployment.md documents the reverse-proxy setup with the host's certbot.

Advantages

  • Removes the largest and most brittle legacy subsystem (container lifecycle, certificate renewal, config templating).
  • Hosts usually already run a web server with TLS; one server block suffices.
  • The generated unit replaces the legacy self-copy/self-update machinery with plain jar deployment.

Disadvantages

  • HTTPS setup is a manual, host-specific step outside werkator's control.

Decision Outcome

All three proposals from the plan were confirmed during implementation and are in force:

  • JSON-file persistence behind BuildResultRepository (steps 01, 05, 06).
  • Server-rendered HTML with polling JSON endpoints and explicit error states (steps 0709).
  • No nginx management; systemd user unit plus reverse-proxy documentation (step 12).

Related, previously decided in the same spirit: external systems are accessed by shelling out to the git and docker CLIs instead of SDK dependencies (steps 02, 11).