Skip to content

Architecture overview ​

FantasyWiki is a monorepo with five deployable or publishable pieces and two shared type packages, orchestrated by Gradle over npm. This page is the map. The rules each piece implements live under domain/; the detail of how each is built lives under architecture/.

Context ​

Who talks to the system, and what it talks to.

Containers ​

Five runtimes. Note what does not connect: the collector has no database credential, and the frontend never talks to the database at all.

The store inside the Cloudflare box is the one production runs on. The box beside it is the same system persisted somewhere else entirely: the backend holds repository interfaces, and MongoDB is a second implementation of every one of them, run locally rather than deployed. Which implementation a build gets is decided in a single module, below.

ContainerRuntimeDeployed byDocumented in
FrontendVue 3 + Ionic SPACloudflare Pages, per branchFrontend
BackendHono on a Cloudflare WorkerWrangler, per branchBackend Architecture
D1SQLite at the edge, reached through a bindingMigrations replayed on deployData model
MongoDBA replica set, reached over the driver — the second target, not deployedIndexes and baseline on first connectionPersistence Targets
Settlement WorkflowCloudflare WorkflowsBundled with the WorkerADR 0003
Scoring CollectorKotlin/JVM, application pluginGitHub Actions cron + GHCR imageScoring Pipeline

Packages ​

Two of the seven directories exist purely so the other five cannot disagree with each other.

model/ holds normalised, framework-free entities, what a Contract is, independent of how it is stored or sent. It imports nothing from a framework, so both a Worker and a browser can use it. → What Are Model Entities

dto/ holds the wire shapes, what the API sends, which is deliberately not the same as what the domain contains. DTOs aggregate and nest; entities stay normalised. Each side "dresses" the domain for its own purpose. → Shared DTO Package · DTO Dressing Pattern

The collector shares neither. It is a JVM process, and giving it generated types would couple a compiled module to a TypeScript build. It gets an HTTP contract and a bearer secret, and that is all it is allowed to know.

The backend's layers ​

Three layers, each talking only to the one below it.

The interesting line is the dotted one. composition.ts is the single place that decides which store the system is running on, it reads a binding and returns one set of implementations, and the rule is enforced mechanically: no-restricted-imports forbids anything under services/, routes/ or tests/ from naming repositories/d1/** or repositories/mongo/**.

That the second implementation exists is what makes this more than an intention. MongoDB was added without a change above the repository layer, and the conformance suite the first target passed became the second's acceptance criteria unchanged. → Persistence Targets

Services may call other services, and are encouraged to: a rule implemented twice is a rule that will eventually be two different rules.

→ Backend Architecture · Backend Error Constants

The seams worth knowing about ​

Each of these is a place where the code was deliberately cut so that one side can change without the other noticing.

SeamWhat it separatesWhy
composition.tsBusiness logic from the databaseSo the store can be replaced without touching a service, and a second one was (the layers, above)
/internal/*The scoring engine from the gameThe collector computes nothing and knows no rules (pipeline)
DraftLineupEditing a formation from saving onePure mutations, testable without a server (lineup editing)
buildArticleDetailArticle facts from viewer contextOwnership is resolved once, asynchronously (ownership resolution)
Wikimedia client capabilitiesTransport from behaviourA capability is added without touching the composition root (client architecture)
Query keys moduleCache identity from call sitesOne module owns every TanStack key (query keys)

Where to go next ​

  • Data flow: sign-in, a request through the layers, a night of scoring
  • Data model: the collections, the derived balance, and the invariants
  • Frontend: state ownership, bootstrapping order, mocking
  • Deployment: branches, environments, and what ships where

Built from the repository's own documentation. Source on GitHub.