API wiring: static feature modules, one clock and per-kind minters, a defined common/, an account/ layer #421
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
spec
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
amaurycoudr/chess-project#421
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem Statement
The API's module wiring and its shared folder cost more than they return. The rules that describe them have fallen behind the code:
forRootto the next. A new dependency between features therefore changes three signatures, and a module's dependencies cannot be read from itsimports.common/has no admission rule. It holds the rate limiter, which only auth uses, and the doorbell, which only the change stream uses. The config folder loads Nest transitively, because it reads the rate-limit defaults from an injectable service file.GuestServiceholds no state and wraps pure functions. Even so, it justifies a global module.account.meandaccount.exportlive in the Games controller andaccount.deletein the Auth controller, because the layer order puts auth left of games.room → rpc. The API standards list four folders where the lint knows seven. ADR 0027 speaks of one flat feature module where there are five.Source: the NestJS audit,
docs/research/nestjs-api-audit.md. Its findings B-11, A-04, A-09, A-11, A-12, B-09, B-10, B-13 and B-19 apply. The owner settled the decisions below in a grilling session (Q1 to Q12).Solution
common/. It holds what at least two features use and no single feature owns. The rate limiter moves to auth, its defaults to config, and the doorbell to room.GuestServiceand its global module.account/composition layer. It answers everyaccount.*procedure.Nothing a Player sees changes.
User Stories
common/, so that a reader finds the decision without reading the lint config.common/holds only what is shared.common/holds only what is shared.common/, so that room, games and the RPC context share them through the layer order.account/layer right of games, so that a procedure composing auth and games has a home the lint allows.account.me,account.exportandaccount.deleteanswered by one Account controller, so that theaccountcontract lives in one place.guest.claimandguest.getto stay where they are, so that theguestcontract does not move with this change.Seams
S1, the wire through the real app (API integration tests,
connect()oncreateApp, requests handed in process). It is extended with token overrides. It covers 7-15, 17-18, 24 and 26-30:account.*procedures answer as before.Existing tests that pass per-feature clocks,
games.randomBytesorguests.mintGuestmove to overrides.S2, the Room through its services (
RoomServiceandPlayServicepublic methods with the Room fixtures). It covers 16 and 22-24: the constructors take the clock and the Room-code and Guest minters directly, in place ofrandom,nowandGuestService.S3, the API lint through its test (the framework-imports lint test). It covers 3, 19-21, 23 and 25:
account/module and controller files may import Nest;The layer order itself stays guarded by
repo/layer-order.No e2e seam: nothing changes that a Player can perceive. The pure Guest functions are tested through S1 and S2, not on their own (root rule 27).
Implementation Decisions
CLAUDE.md, "No rule edits in feature work"). It lands before any code.common → config → mail → auth → games → account → devtools → room → ..common/admits what at least two features use and none owns.common/rule;auth/,mail/,devtools/andaccount/that may import Nest;Date.now,Math.randomorrandomBytes;createApp's overrides.isClaimedandonDecidedcallbacks stay as they are.common/.randomBytesorMath.random. Services inject what they use instead of takingnow,randomorrandomBytesoptions.createApptakes an optional second argument, a list of{ token, value }pairs applied as provider overrides before the app boots.connect()passes its overrides there.common/contents.GuestServiceand its global module go. A framework-free file incommon/named after Guest holds the occupant types, the occupant of a Guest (its hash) and the comparison of two occupants.GuestService.Occupantstays as a code type; the glossary is not changed.account/.account.me,account.exportandaccount.delete. It injects the Auth and Games services.Testing Decisions
test/rpc.fixtures.tsand the RPC integration tests (S1),room.fixtures.tswith the Room tests (S2), andtest/framework-imports.lint.test.ts(S3).account.me,account.exportandaccount.deleteanswer as before, signed in and signed out;account/file, in both directions.Out of Scope
guest/ordatabase/folder: the owner chose the "shared" rule forcommon/.Occupant, or adding it to the glossary.GamesService(B-04, B-05).Further Notes
The audit and its research files are
docs/research/nestjs-api-audit.md,nestjs-practices.mdandnestjs-opinion.md. They are not merged yet.Planned tickets, in order:
account/.Tickets 2, 3 and 5 depend on 1 only; ticket 4 depends on 3.
mentioned in issue #422
mentioned in issue #423
mentioned in issue #424
mentioned in issue #425
mentioned in issue #426
mentioned in issue #427
mentioned in issue #428
marked this issue as related to #422
marked this issue as related to #423
marked this issue as related to #424
marked this issue as related to #425
marked this issue as related to #426
marked this issue as related to #427
marked this issue as related to #428
mentioned in commit
7a1f8785f5mentioned in commit
f669e0339fmentioned in commit
817ef0d477mentioned in merge request !536
mentioned in commit
52f99ce936mentioned in commit
658857e3cfmentioned in commit
7388250063