2D Game Engine

Decyzje architektoniczne

Dziewięć rekordów ADR — decyzje, których cofnięcie kosztowałoby więcej niż tydzień pracy. Kontekst, decyzja, konsekwencje.

Po co ADR#

Każda decyzja, której cofnięcie kosztowałoby więcej niż tydzień pracy, ma swój rekord. Format jest stały: Kontekst → Decyzja → Konsekwencje → Odrzucone alternatywy. Status przyjęta oznacza, że kod i dokumentacja są z nią zgodne.

Lista decyzji#

NrDecyzjaStatus
0001Izolacja adaptera przez portyprzyjęta
0002Wersjonowany format JSON z łańcuchem migracjiprzyjęta
0003Brak ECS — prototyp oceniony i odrzuconyprzyjęta
0004WASM opcjonalny, tylko przy mierzalnej przewadzeprzyjęta
0005Brak SPA fallback — stan w query stringuprzyjęta
0006Jedna wersja dla wszystkich pakietówprzyjęta
0007Bramy jakości jako blokujące komendyprzyjęta
0008Playground jako jeden klasyczny plik HTMLprzyjęta
0009Dokumentacja jako generowana jedna stronaprzyjęta

0001 — Izolacja adaptera przez porty#

Decyzja: rdzeń nie importuje Phasera; cały runtime idzie przez sześć interfejsów (RenderPort, PhysicsPort, CameraPort, InputPort, AudioPort, AssetBackend).

Konsekwencje: kod gry testowalny w Node, wymienny backend, ale dodatkowa warstwa abstrakcji i konieczność feature-detection dla metod opcjonalnych. Pilnują tego ESLint i test skanujący źródła.

0002 — Wersjonowany format JSON#

Decyzja: projekt ma formatVersion i łańcuch migracji; eksport zawiera sumę FNV-1a.

Konsekwencje: stare projekty otwierają się w nowych wersjach silnika; migracje są addytywne i idempotentne.

0003 — Brak ECS#

Decyzja: prototyp ECS został zbudowany, zmierzony i odrzucony. Zostają komponenty z fazami systemowymi.

Konsekwencje: prostszy model mentalny i przewidywalna kolejność faz przy zachowaniu wydajności (0,72 ms/klatkę dla 20 000 obiektów). Pełny ECS wymagałby przebudowy indeksów i serializacji.

0004 — WASM opcjonalny#

Decyzja: most WASM istnieje, ale moduł dodajemy tylko po benchmarku TS vs WASM.

Konsekwencje: required: false nie wywala gry przy braku pliku; zespół nie utrzymuje natywnego kodu „na wszelki wypadek”.

0005 — Brak SPA fallback#

Decyzja: gra żyje pod stałym adresem katalogu, stan trzyma w query stringu; nieznane ścieżki zwracają 404.html.

Konsekwencje: względne ./assets/… działają w dowolnym podkatalogu; deep-linking po ścieżkach nie istnieje i nie jest potrzebny.

0006 — Jedna wersja dla wszystkich pakietów#

Decyzja: wszystkie pakiety w monorepo mają tę samą wersję (lockstep).

Konsekwencje: zero macierzy kompatybilności wewnętrznej; release jednego pakietu oznacza release wszystkich.

0007 — Bramy jakości jako blokujące komendy#

Decyzja: typecheck, test, bench:check i bundle:check kończą się kodem 1 przy problemie.

Konsekwencje: CI nie potrzebuje parserów wyjścia; regresja wydajności i rozmiaru blokuje merge tak samo jak błąd typów.

0008 — Playground jako jeden plik HTML#

Decyzja: npm run build:single tworzy playground.html działający bez sieci.

Konsekwencje: grę można wysłać mailem i otworzyć z dysku; próg rozmiaru single-file to +5%.

0009 — Dokumentacja jako generowana jedna strona#

Decyzja: kanoniczna dokumentacja to generowany docs/index.html z wyszukiwaniem i PL/EN.

Konsekwencje: dokumentacja działa offline i bez serwera; źródła pozostają w Markdown.

Jak dodać rekord#

  1. Numer kolejny, czterocyfrowy, plik NNNN-krotki-opis.md.
  2. Wpis w tabeli indeksu — scripts/release.mjs --check zgłasza rekord bez wpisu.
  3. Sekcje: Kontekst, Decyzja, Konsekwencje (także koszty), Odrzucone alternatywy.
  4. Zmiana decyzji = nowy rekord ze statusem zastępuje 000N, nie edycja historii.