共通規約 Conventions
Parky プロジェクト全体で守るべき共通ルールです。DB設計・コード・運用まで含みます。
Shared rules across the Parky project — covers DB design, code, and operations.
1. コードマスター方針1. Code-master strategy
ステータス・カテゴリ・種別などの列挙値は、日本語ラベルではなく
コード値(英語小文字スネークケース)で DB に保存します。
表示ラベルは codes テーブル(コードマスター)で管理し、クライアント側で解決します。
Enum-like values (status, category, type…) are stored as lowercase snake_case code values,
not Japanese labels. Display labels come from the codes table and are resolved client-side.
codes schema
category_id -- 分類ID (e.g. 'admin_status')
code -- コード値 (e.g. 'active')
display_label -- 表示名 (e.g. '有効')
lang -- 言語コード (default 'ja')
sort_order -- 表示順
is_deleted -- 削除フラグ
フロントエンドでは useCode() フックで取得し、
code.label('admin_status', value) で表示名に変換、
code.options('admin_status') でドロップダウン選択肢を生成します。
Frontends use a useCode() hook:
code.label('admin_status', value) → label, and
code.options('admin_status') → dropdown options.
カテゴリの代表例: admin_status, user_status, vehicle_type,
session_status, notif_type, notif_status, notif_target,
admin_notif_category, device_type, lot_status, lot_structure,
payment_status, badge_category, theme_part_category,
ticket_status, article_status, review_status, boost_status,
revenue_channel ほか多数(実測で 50 以上)。
実際の完全なカテゴリ一覧は codes テーブルの SELECT DISTINCT category_id FROM codes で取得する。
Representative categories: admin_status, user_status, vehicle_type,
session_status, notif_type, notif_status, notif_target,
admin_notif_category, device_type, lot_status, lot_structure,
payment_status, badge_category, theme_part_category,
ticket_status, article_status, review_status, boost_status,
revenue_channel, and many more (50+ total).
For the full list query SELECT DISTINCT category_id FROM codes.
2. ソフトデリート方針2. Soft delete
ユーザーが直接操作する削除は ソフトデリート(deleted_at カラム)を使います。
完全削除は「ゴミ箱」画面からのみ実行可能にします。
User-initiated deletes use soft delete via a deleted_at column.
Hard deletes are only possible from a Trash UI.
3. リンクテーブル(多対多)3. Link tables for M:N
エンティティ間の多対多・参照関係はリンクテーブル(中間テーブル)で持ち、 冗長な外部キーやデータコピーを避けます。
Many-to-many relations live in link tables — no redundant foreign keys or duplicated data.
例: parking_sessions は user_id + parking_lot_id で
app_users と parking_lots を参照します。
Example: parking_sessions references app_users and parking_lots
via user_id + parking_lot_id.
4. べき等性4. Idempotency
すべてのスクリプト・SQL・マイグレーション・シード・データ操作はべき等でなければなりません。
Every script, SQL statement, migration, seed, and data operation must be idempotent.
- SQL:
CREATE TABLE IF NOT EXISTS/INSERT ... ON CONFLICT DO UPDATE - SQL:
CREATE TABLE IF NOT EXISTS/INSERT ... ON CONFLICT DO UPDATE - スキーマ変更はテーブル単位のマスターファイルに追記/上書き(バージョン番号のついたファイルは作らない)
- Schema changes append to per-table master files — no versioned filenames.
- 一時ツールは処理完了後に削除する。繰り返し使うものだけ
scripts/に残す。 - Throw-away tools are deleted after use; keep only reusable ones under
scripts/.
5. 環境変数・シークレット5. Env vars & secrets
- 本番
.envは直接編集せず、.env.exampleをコピーして値を埋める - Never edit production
.envdirectly — copy.env.exampleand fill values. - Claude に渡したいがGit管理したくない情報は
.claude/context/に置き、.gitignore対象にする - Secrets/context for Claude live under
.claude/context/and are gitignored. - シークレットをコード内・CLAUDE.md 内にハードコードしない
- Never hardcode secrets in source or in CLAUDE.md.
6. 命名規則6. Naming
- DBテーブル・カラム:
snake_case - DB tables and columns:
snake_case. - リンクテーブル:
owner_table_singular+related_table_plural(例:parking_lot_tags) - Link tables:
owner_singular+related_plural(e.g.parking_lot_tags). - TypeScript/Dart: 型は
PascalCase、関数・変数はcamelCase - TypeScript/Dart: types in
PascalCase, fns/vars incamelCase. - ブランチ:
devが統合先。個別作業はfeature/.../fix/... - Branches: integrate into
dev. Usefeature/.../fix/...for work.
7. コミット・デプロイ7. Commits & deploy
- コミットメッセージは日本語で、変更内容と目的が分かるように書く
- Commit messages are written in Japanese, with clear intent and reason.
- 修正が完了したらコミット+プッシュまで自動で行う
- When a fix is ready, commit and push automatically.
- デプロイは GitHub Actions 経由のみ。直接 scp/rsync はしない
- All deploys go through GitHub Actions — no manual scp/rsync.
8. 一時作業ファイル8. Scratch outputs
- 一時的なダンプ・解析結果は
.work/{プロジェクト}/に出力(サブフォルダ必須) - Scratch outputs go under
.work/{subfolder}/(never at the root). - ファイル名は
YYYY-MM-DD_HH-MM_{日本語名}.{ext} - Filenames:
YYYY-MM-DD_HH-MM_{ja-name}.{ext}.
9. 層規律(parky/api/ 内部構造)9. Layer discipline (internal structure of parky/api/)
Parky BFF(parky/api/)は 4 層構造
(bff / core / data / schema)+ shared / app で固定する。詳細決定は
ADR-0005 (Layer-First の機械強制)。
The Parky BFF (parky/api/) is organised as a 4-layer structure
(bff / core / data / schema) + shared / app. See
ADR-0005 (mechanical enforcement of Layer-First).
9.1 ディレクトリ構造9.1 Directory structure
parky/api/src/
├── bff/ ← 画面 / endpoint 単位
│ ├── mobile/{views,actions,telemetry}/
│ ├── web/ admin/ owner/ marketing/
├── core/ ← capability 単位
│ ├── parking-lots/ parking-sessions/ reviews/ gamification/
│ ├── subscriptions/ users/ auth/ notifications/
│ ├── pricing/ ← cross-domain orchestration
│ ├── home-feed/ ← cross-domain aggregate
│ └── search/
├── data/ ← table cluster 単位(1 ファイル 1 table)
│ └── <table>.data.ts
├── schema/
│ ├── domain/ ← core 用(camelCase の意味論型)
│ ├── view/ ← bff 用(ViewEnvelope data 部分)
│ └── row/ ← data 用(snake_case の DB 列型)
├── shared/{lib,middleware,schema}/
└── app/
├── routes-manifest.ts
└── index.ts
9.2 依存方向(厳守)9.2 Dependency direction (strict)
- 正方向:
bff → core → data。逆参照は lint エラー - Forward:
bff → core → data. Reverse imports are lint errors. schema/viewを import できるのはbffのみ(core / data は禁止)- Only
bffmay importschema/view(core / data forbidden). schema/rowを import できるのはdataのみ(core / bff は禁止)- Only
datamay importschema/row(core / bff forbidden). schema/domainは全層から import 可(共通の意味論)schema/domainis importable from any layer (shared semantics).bff → data直呼びは単純 read-only CRUD に限り許容(ロジック出現時は即 core 追加)bff → datadirectly is allowed only for pure read-only CRUD; add core the moment logic appears.
9.3 各層で書いていいこと / 書いてはいけないこと9.3 What each layer may / must not do
| 層 | Layer | OK | OK | NG | NG |
|---|---|---|---|---|---|
bff/ |
bff/ |
Zod schema / ViewEnvelope 組立 / core 呼出 | Zod schemas / ViewEnvelope assembly / calling core | SQL 直書き / if によるビジネス判定 / data 直叩き(単純 CRUD 除く) |
SQL / if-based business decisions / direct data calls (except pure CRUD) |
core/ |
core/ |
domain 演算 / 複数 data の orchestration / 他 core 呼出 / domain → view 変換 |
Domain operations / orchestrating multiple data calls / calling sibling core / domain → view mapping |
SQL 直書き / HTTP 応答知識 / schema/view への逆依存 |
SQL / HTTP response knowledge / importing schema/view |
data/ |
data/ |
SQL / postgres.js / Row → domain 変換 | SQL / postgres.js / Row → domain translation | ビジネスルール / 複数 table の混在(1 ファイル 1 table cluster)/ HTTP 応答知識 | Business rules / mixing multiple tables in one file / HTTP response knowledge |
9.4 「テーブル変更が BFF に染みない」保証9.4 Guarantee that table changes don't leak into BFF
app_users.display_name を nickname に rename するケースを例に:
Example: renaming app_users.display_name to nickname:
schema/row/app-users.row.tsの列をnicknameに変更- Update the column in
schema/row/app-users.row.tstonickname. data/app-users.data.tsの SQL と row → domain 変換を修正(displayName: row.nickname)- Update SQL and row → domain mapping in
data/app-users.data.ts(displayName: row.nickname). schema/domain/app-user.tsは touched しない(意味論displayNameを維持)schema/domain/app-user.tsis not touched — the semantic namedisplayNamestays.core/とbff/はまったく修正不要- No changes anywhere in
core/orbff/.
9.5 アンチパターン9.5 Anti-patterns
bffでif (user.isPremium) ...— core がcanAccessPremiumFeature: trueを返して bff は横流しif (user.isPremium) ...inbff— core should returncanAccessPremiumFeature: true; bff is a passthrough.coreで SQL 直書き —dataに移譲- SQL in
core— delegate todata. - 1
dataファイルで複数 table を mix — 1 ファイル 1 table cluster - Mixing tables inside one
datafile — keep one table cluster per file. schema/viewを core が import — core は domain で完結させるcoreimportingschema/view— core must be self-contained in domain types.schema/rowを core / bff が import — DB 列名を層外に露出させないcore/bffimportingschema/row— DB column names must not leak out ofdata.core配下に水平 layer を切る(core/x/services/)— フラットにして必要なら_internal/のみ- Adding a horizontal sub-layer inside
core(core/x/services/) — keep it flat; the only allowed sub-folder is_internal/. - 何でも
shared/に放り込む — 3 layer 以上が本当に共通参照するか gating - Dumping everything into
shared/— gate on "at least 3 layers actually depend on it".
9.6 機械化9.6 Enforcement
- ESLint
no-restricted-importsで layer 境界違反を検出 - ESLint
no-restricted-importscatches boundary violations. madgeで循環依存検出、dependency-cruiserで逆流検出を CI に組み込むmadgefor circular deps;dependency-cruiserfor reverse-flow detection — both wired into CI.- PR は layer 規約違反があるとブロックされる
- PRs are blocked on layer violations.