refactor: enforce package boundaries

This commit is contained in:
2026-08-07 10:13:29 +00:00
parent b1de142440
commit 1a1d0b2d91
64 changed files with 812 additions and 628 deletions
+5
View File
@@ -76,6 +76,11 @@ persistence는 `app/game-engine`이 제공합니다.
`src/gatewayPrisma.ts`, `src/gamePrisma.ts`, `src/postgres.ts`, `src/redis.ts`
연결과 client 생성을 담당합니다.
구체적인 import 방향과 파일 배치 기준은
[패키지와 파일 경계](./package-boundaries.md)를 따릅니다. 순수 거리 계산과
도메인 로그 enum은 `packages/logic`, resource 파일 loader와 trace 출력은
app/infra adapter가 소유합니다.
## 데이터 소유권
| 데이터 | 기준 저장소 | 주요 접근 경로 |
+67
View File
@@ -0,0 +1,67 @@
# 패키지와 파일 경계
## 의존 방향
제품 소스의 의존 방향은 다음과 같습니다.
```text
packages/common
packages/logic ← packages/infra
↑ ↑
└──── app/game-engine ────┐
↑ │
app/game-api app/gateway-api
↑ ↑
game-frontend gateway-frontend
```
화살표의 시작점이 끝점을 import합니다. `packages/logic`은 DB, Redis, 파일,
네트워크, 환경 변수와 stdout을 직접 사용하지 않습니다. 런타임 관찰이 필요한
경우 `packages/logic/src/ports/`에 포트를 선언하고 app 계층에서 구현을
주입합니다. Prisma 생성 타입과 connector는 `packages/infra`가 소유하며,
도메인 enum과 규칙은 생성 client에서 다시 export하지 않습니다.
## 위치를 정하는 기준
| 위치 | 포함하는 코드 | 포함하지 않는 코드 |
| ----------------- | ----------------------------------------------------------- | ------------------------------------------------------- |
| `packages/common` | process 사이 직렬화 타입, 인증 token, 결정적 RNG, 범용 함수 | DB client, 파일 loader, 게임 mutation |
| `packages/logic` | 명령·전투·AI 계산, domain type, constraint, port interface | Prisma/Redis, `process.env`, stdout, 파일·HTTP 접근 |
| `packages/infra` | Prisma 생성 client, PostgreSQL/Redis connector와 repository | 도메인 규칙, API 인증·validation, process orchestration |
| `app/game-engine` | daemon 조립, resource loader, in-memory state, transaction | 재사용 가능한 순수 계산의 유일 구현 |
| `app/game-api` | tRPC/SSE, 인증, request validation, worker transport | daemon process entrypoint의 암묵 실행 |
| `app/gateway-api` | 계정·profile 정책, operation queue, PM2 orchestration | game-engine process entrypoint의 암묵 실행 |
| `app/*-frontend` | 브라우저 UI, store, 공개 API client | Node/DB runtime과 backend value import |
Resource를 읽는 `scenarioLoader`, `mapLoader`, `unitSetLoader`,
`turnCommandProfile`은 game-engine runtime adapter가 소유합니다. 다른 app은
동일 loader를 복사하지 않고 `@sammo-ts/game-engine/...`의 구체적인 subpath를
사용합니다. `@sammo-ts/game-engine` 루트는 daemon process entrypoint이므로
API 제품 소스에서 library처럼 import하지 않습니다.
Frontend가 tRPC router shape를 참조할 때는 `import type`만 사용하고 backend
package를 `devDependencies`에 둡니다. 브라우저에서 실제 실행하는 공유 값만
`common` 또는 `logic`의 browser-safe export에서 가져옵니다.
## 자동 검사
```sh
pnpm check:architecture
pnpm test:architecture
```
`check:architecture`는 모든 `packages/*/src``app/*/src`를 읽어 다음을
검사합니다.
- 허용되지 않은 workspace package 의존
- common/logic의 DB·Redis·파일·네트워크 import와 직접 `fetch`
- logic의 직접 환경 변수·stdout 접근
- frontend의 Node import와 backend value import
- API의 game-engine 루트 entrypoint import
- `infra`에서 도메인 로그 enum을 가져오는 코드
- source import와 `package.json` dependency 종류의 불일치
Integration/E2E fixture는 실제 DB와 Node 파일 API를 사용할 수 있으므로 제품
`src` 검사와 분리합니다. 테스트 예외는 제품 코드의 경계를 완화하는 근거가
아닙니다.
+6 -1
View File
@@ -33,12 +33,14 @@ core2026/
| turn daemon | `app/game-engine/src/turn/turnDaemon.ts` | lifecycle, loader, handler, flush |
| daemon lease | `app/game-engine/src/lifecycle/databaseTurnDaemonLease.ts` | `TurnDaemonLease` |
| world load·flush | `worldLoader.ts`, `databaseHooks.ts` | `EngineStateManager`, game Prisma |
| package 경계 | `tools/check-package-boundaries.mjs` | source import, manifest dependency |
| 장수·국가 명령 | `packages/logic/src/actions/turn` | constraint, command module, engine handler |
| 전투 | `packages/logic/src/war` | action module, crew type, item, trait |
| 지도 거리 계산 | `packages/logic/src/world/distance.ts` | AI·명령이 공유하는 순수 BFS |
| 월간 처리 | `app/game-engine/src/turn/monthly*.ts` | scenario event, world dirty state |
| frontend route | `app/*-frontend/src/router/index.ts` | view, store, tRPC client |
| schema | `packages/infra/prisma/*.prisma` | migration, client, loader |
| resource | `resources/` | scenario/map/unit-set loader |
| resource | `resources/` | `app/game-engine/src/scenario/*Loader.ts` |
## 변경 단위
@@ -53,6 +55,9 @@ Router의 input schema, procedure, actor 해석, transaction과 error를 먼저
ref entry point, SQL, RNG, log와 mutation 순서를 조사합니다. 순수 계산은
`packages/logic`, 실행 context와 persistence는 `app/game-engine`에 둡니다.
Fixed-seed unit, 실제 DB integration과 ref 차등 fixture를 함께 갱신합니다.
새 package import나 파일 이동은
[패키지 경계 문서](../architecture/package-boundaries.md)와
`pnpm check:architecture`로 확인합니다.
### DB
+2
View File
@@ -8,6 +8,7 @@
| process·worker·daemon | [런타임 아키텍처](../architecture/runtime.md) | app server와 CLI |
| profile·Gateway 배포 | [릴리스 운영 매뉴얼](../release-operations.md) | Admin GUI와 release-controller |
| 파일 위치 | [파일 지도](./code-map.md) | router, handler, schema |
| package 의존 방향 | [패키지와 파일 경계](../architecture/package-boundaries.md) | `packages/*`, `app/*` |
| 명령·전투·효과 | [도메인과 조립](./domain-and-classes.md) | `packages/logic` |
| mutation·flush | [요청·턴·저장](./request-turn-persistence.md) | game API, game engine |
| action module | [행동 모듈 프로토콜](../architecture/action-module-protocol.md) | `actionModules/` |
@@ -16,6 +17,7 @@
## 경계
- `packages/logic`은 계산과 규칙을 소유합니다.
- runtime I/O는 logic의 port를 app/infra adapter가 구현해 주입합니다.
- `app/game-engine`은 clock, queue, AI, 월간 순서, transaction과 flush를
소유합니다.
- `app/game-api`는 transport, 인증, input validation과 request acceptance를
+2
View File
@@ -34,6 +34,8 @@ features:
Gateway 배포는 [릴리스 운영 매뉴얼](./release-operations.md)을 따라 주세요.
게임 진행 시각과 운영 벽시계의 경계는
[게임 시계](./architecture/game-clock.md)에 설명합니다.
[패키지와 파일 경계](./architecture/package-boundaries.md)는 source import와
폴더별 책임, 자동 검사 방법을 설명합니다.
Ref 전용 수치·저장 표현 보정과 제거 절차는
[Ref 호환 shim 인벤토리](./ref-compatibility-shims.md)에 모아 둡니다.