First commit, phased 1 and 2 created but untested
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# ARCHITECTURE.md
|
||||
|
||||
## Layering (strict, one-way)
|
||||
```
|
||||
routes → services → game (pure rules)
|
||||
↓ ↓
|
||||
middleware db (prepared statements)
|
||||
```
|
||||
- **routes/**: parse + validate request, call a service, shape the response. No business logic. No SQL.
|
||||
- **services/**: orchestrate. Open transactions, read/write DB, call `game/` functions, return plain objects.
|
||||
- **game/**: pure functions. No `require` of db/express. Deterministic given inputs (RNG is passed in).
|
||||
- **db/**: connection, migrations, seeds. Only services import `db`.
|
||||
- **middleware/**: auth guard, rate limiting, error handler, request validation helper.
|
||||
- **utils/**: tiny helpers (rng, time, ids, logger).
|
||||
|
||||
Dependencies only point downward. `game/` never imports from `services/`.
|
||||
|
||||
## Folder structure
|
||||
```
|
||||
emberfall/
|
||||
├── NOTES.md STATUS.md README.md
|
||||
├── package.json .env .env.example .gitignore
|
||||
├── docs/ # specs (this folder)
|
||||
├── data/ # SQLite file lives here (gitignored)
|
||||
├── scripts/
|
||||
│ ├── migrate.js # runs src/db/migrations/*.sql in order
|
||||
│ ├── seed.js # runs src/db/seeds/*.js (idempotent)
|
||||
│ └── seed-dev.js # demo account + hero
|
||||
├── src/
|
||||
│ ├── server.js # listen()
|
||||
│ ├── app.js # builds express app (exported for tests)
|
||||
│ ├── config/index.js
|
||||
│ ├── db/
|
||||
│ │ ├── index.js # better-sqlite3 handle
|
||||
│ │ ├── migrations/ # 001_users.sql, 002_characters.sql ...
|
||||
│ │ └── seeds/ # zones.js, monsters.js, items.js, expeditions.js
|
||||
│ ├── middleware/
|
||||
│ │ ├── requireAuth.js
|
||||
│ │ ├── rateLimiters.js
|
||||
│ │ ├── validate.js # zod wrapper
|
||||
│ │ └── errorHandler.js
|
||||
│ ├── routes/
|
||||
│ │ ├── index.js # mounts all routers under /api
|
||||
│ │ ├── auth.js character.js hunt.js inventory.js
|
||||
│ │ ├── shop.js expeditions.js leaderboard.js heroes.js
|
||||
│ ├── services/
|
||||
│ │ ├── authService.js characterService.js huntService.js
|
||||
│ │ ├── inventoryService.js shopService.js
|
||||
│ │ ├── expeditionService.js leaderboardService.js
|
||||
│ ├── game/
|
||||
│ │ ├── constants.js # ALL tunable numbers live here
|
||||
│ │ ├── classes.js stats.js energy.js combat.js
|
||||
│ │ ├── rewards.js leveling.js loot.js
|
||||
│ └── utils/
|
||||
│ ├── rng.js time.js errors.js logger.js
|
||||
├── public/
|
||||
│ ├── index.html login.html register.html
|
||||
│ ├── create-hero.html game.html shop.html leaderboard.html
|
||||
│ ├── css/ (base.css, theme.css, layout.css, components.css)
|
||||
│ ├── js/ (api.js, auth.js, game.js, ui.js, ...)
|
||||
│ └── img/
|
||||
└── tests/
|
||||
├── unit/ # src/game/* and utils
|
||||
└── integration/ # HTTP-level tests using a temp DB
|
||||
```
|
||||
|
||||
## Module format
|
||||
Use **CommonJS** (`require` / `module.exports`) everywhere on the server, to keep
|
||||
tooling simple. Frontend JS uses plain `<script>` tags or ES modules (`type="module"`) — pick one and stay consistent (log in DECISIONS.md).
|
||||
|
||||
## Request lifecycle
|
||||
1. `helmet` → JSON parser → session → static files
|
||||
2. `/api/*` routers; protected routers use `requireAuth`
|
||||
3. Route validates body/query with zod
|
||||
4. Service runs (transaction for anything that writes more than one row)
|
||||
5. Route sends `{ ok: true, data: ... }`
|
||||
6. Any thrown `AppError` → `errorHandler` → `{ ok: false, error: {code, message} }`
|
||||
|
||||
## Transactions
|
||||
Any action that spends a resource AND grants a result (hunt, buy, claim) is ONE
|
||||
`db.transaction(() => { ... })()`. better-sqlite3 is synchronous, so this also
|
||||
prevents double-spend races from parallel requests.
|
||||
|
||||
## Time
|
||||
All timestamps are **integer milliseconds since epoch (UTC)**, stored as INTEGER.
|
||||
`src/utils/time.js` exports `now()` so tests can stub it. Never call `Date.now()`
|
||||
directly in services or game code.
|
||||
|
||||
## Config
|
||||
Gameplay numbers → `src/game/constants.js`. Environment/infra → `src/config/index.js`.
|
||||
Never hardcode magic numbers inline.
|
||||
Reference in New Issue
Block a user