Code Server - SekiroKenjii/Quizzivy GitHub Wiki
Go module quizzivy: a modular monolith. One process, one database, eleven
bounded contexts that talk to each other only through the ports they declare.
The decision record is docs/plan/60-backend-architecture.md; this file is the
map.
server/
cmd/ entry points only: api (the server), import-worker, maintenance, migrate,
seedadmin, and the Word tools (word-convert, word-extract, word-inspect,
word-recognize, verify-import-storage)
internal/
core/ the composition root: wiring/ builds each module, router/ fronts them,
adapters/ bridge ports, jobs/ run background commands, maintenance/ holds
the operator's commands cmd/maintenance runs
platform/ technical adapters: config, db (pgx; the Context and Repository every module embeds),
storage (S3), google (OIDC), probe (audio), httpx (middleware), httpserver,
ratelimit, httpapi (transport helpers), apidocs (Scalar)
shared/ the kernel every layer may use: cqrs (handler contracts), actor, paging, audit,
stats, opt, validation, content (versioned learner content), access (the
permission model: keys, requirements, the principal and its scope),
visibility (who a teacher reaches)
modules/ the bounded contexts
identity/ User aggregate, sessions, the teacher's roster of students, PasswordManager
classes/ Class aggregate, members, join codes (JoinCodeManager, CodeState, JoinCodeKeys)
questions/ Question aggregate with options and blanks, QuestionManager
media/ Asset aggregate in object storage, AssetManager
tests/ Test aggregate, drafts and published versions, PublishManager
assignments/ Assignment aggregate, policies, ScheduleManager
attempts/ Attempt aggregate: the deal, answers, grading, review, monitor, timeline
dashboard/ read models for the teacher's home (no aggregate)
imports/ Word and PDF imports: private sources, processing runs, the review draft, the commit
availability/ the maintenance windows the operator schedules, as the API reads them
access/ roles and their grants, PermissionManager, the cached principal (no transport until R5)
tests/ end-to-end: the whole application in-process, over HTTP
gen/openapi/ generated from api/openapi.yaml, committed, never hand-edited
| layer | holds | may import |
|---|---|---|
domain/ |
aggregate root, entities, value objects, commands, errors, the repository interface, and the managers — the domain's rules that belong to no single entity |
shared, other modules' domain
|
application/ |
the use cases as commands and queries: command/ and query/ hold one struct and one handler per use case, ports/ what the module needs from outside, model/ the results other modules read, internal/support/ what the handlers share; app.go builds Application{Commands, Queries}
|
own domain, other modules' domain and application, shared
|
repositories/ |
Postgres, implementing the domain interface; SQL lives here and nowhere else | own domain, platform, shared
|
http/ |
the operations this module serves, embedded into core's composite strict server; presenters other transports reuse | own domain/application, platform/httpx, platform/httpapi, gen/openapi, other modules' domain, application and http
|
core/tests/architecture_test.go fails the build on any import in the wrong
direction. Managers are reached as package values (domain.Grading.Grade,
domain.JoinCodes.Normalize); applications are constructed by core/wiring
with their repository and ports, and a transport reaches a use case as
h.app.Commands.Create.Handle(ctx, command.Create{…}) or
h.app.Queries.Get.Handle(ctx, query.Get{…}). A port another module fills with
one operation is typed as that module's handler (identity's SelfEnroller is
the classes EnrolNewMember command; the attempts transport's Students is the
identity StudentAccount query).
Three tiers, three build tags, one directory convention: a layer's tests live
in <layer>/tests/ as an external package and use only what the layer
exports. A test that needs a private function is a test of the wrong thing.
| tier | tag | where | needs | runs with |
|---|---|---|---|---|
| unit | none | <layer>/tests/ |
nothing | make test-api-unit |
| integration | integration |
repositories/tests/, application/tests/
|
TEST_DATABASE_URL |
make test-api-integration |
| end-to-end | e2e |
server/tests/ |
TEST_DATABASE_URL, and MinIO (make up) for the isolation suite |
make test-api-e2e |
make test-api runs all three in that order; CI runs them as three steps.
The server serves its own Scalar reference at /docs, loading the contract it
was built from at /docs/openapi.json. The page is platform/apidocs; no
package wraps it, and the Scalar version is pinned there beside its SRI hash.
Both routes are admin only (spec §5.5): open them from the web app's
Settings → Preferences → "Tài liệu API", which mints a fifteen-minute docs
cookie. For local development, DOCS_PUBLIC=true in .env skips that check;
the server refuses it in production.
Generated from server/README.md. Edit that file in the repository; changes made on this page are overwritten.