リファクタリング方針(共通化)
コードの重複を減らし、顧客PWA・店員POS・Terminal・サーバー・テストで同じ処理を使い回すための構成。
1. 共有パッケージ
| パッケージ |
役割 |
packages/api-types |
API DTO(ShopConfig / CatalogItem / OrderSummary / FeedOrder / RegisterState / StaffSession / TerminalSession 等)。型のみ |
packages/api-client |
ApiError / request() / formatApiError() / createPoller() |
packages/format |
yen() / formatJst() / formatUtc() / formatElapsed() |
packages/cart |
カート演算(add/change/remove/total/toOrderItems/バーコード解決) |
packages/ui-cart |
ProductGrid.svelte / CartLines.svelte |
packages/core |
ドメイン型・状態遷移・価格・コード・ULID・newClientToken(/token サブパス) |
packages/terminal-ui |
Terminal モックUI(実機比率の端末フレーム) |
- ブラウザから
@shop-tools/core の値を import すると node:crypto を巻き込むため、newClientToken は @shop-tools/core/token を使う
api-types は型のみなので、サーバー・アプリ双方から安全に参照できる
2. サーバー構成
共通ヘルパー
| ヘルパー |
役割 |
lib/orderAccess.ts |
requireShopOrder() / resolveTerminalId() / assertRegisterAvailable() |
lib/events.ts |
emitOrderEvent()(toFeedOrder を内包) |
lib/orderInput.ts |
注文明細の解決(価格・数量検証) |
lib/hmac.ts |
HMAC署名(注文トークンとWSチケットで共用) |
lib/shopView.ts |
toShopConfig()(config / bootstrap で共用) |
lib/ddb.ts |
putIfAbsent() |
lib/db.ts |
rawClient() / doc() |
lib/notification/ |
NotificationTransport(LocalTransport / ApiGatewayTransport) |
ディレクトリ分割
server/src/
repo/orders/ create / read / write / mapper / index
api/routes/staff/ auth / orders / register / ws / index
repo/orders と routes/staff は index.ts で再エクスポートし、既存のimportパスを維持
- 通知は
wsMode に応じて transport を選択(将来 SQS/SNS 追加が容易)
3. クライアント構成
apps/customer/src/
App.svelte # レイアウトのみ
lib/session.svelte.ts # カタログ/カート/注文/キャッシュ
lib/api.ts # api-client 利用
components/ CatalogView / CartView / CodeView / DoneView / CartBar
apps/staff/src/
App.svelte # レイアウト・タブ・配線のみ
lib/session.svelte.ts # セッション/選択注文/ロック/ポーリング
lib/feed.svelte.ts # WS/フィード/フォールバック
lib/posCart.svelte.ts # POSカート
lib/beep.ts # 通知音
components/ LoginCard / BlockingBanner / FeedPane / PosPane / CheckoutPane
- 状態はセッション(または専用ストア)に集約し、画面は props 中心
- ポーリングは
createPoller() に統一(顧客/Terminal)
Vite / Svelte 設定
tsconfig.app.json を各アプリで extends(共通)
- Vite / Svelte 設定はアプリごとに保持(workspace の依存解決で共通化すると
@sveltejs/vite-plugin-svelte の解決に失敗するため)
4. テスト・ツール
| 対象 |
内容 |
tests/e2e/lib/harness.mjs |
record / summary / call / startProcess / waitForHttp / killTree / randomPort |
tests/e2e/ |
api-flow / local-flow / ui-smoke がハーネスを共用 |
tools/local-api/ |
env.mjs(.env/env.local)/ event.mjs(Lambdaイベント整形)/ ws.mjs(WS)/ server.mjs |
tools/demo-launcher/ |
net.mjs(LAN IP/probe)/ page.mjs(HTML)/ server.mjs |
5. スクリプト
scripts/_common.ps1: Get-RepoRoot / Add-ToolPaths / Invoke-BuildServer / Wait-TcpPort / Start-LocalDatabase
api.ps1 / local.ps1 / deploy.ps1 / db.ps1 / stop.ps1 は共通関数を利用
- PowerShell 5.1 のため UTF-8 BOM を付与
npm run verify で「DB起動→型→unit→統合→ローカルE2E→UI→build」を一括実行
6. 設定の正(どこを直すか)
| 値 |
ローカル開発 |
AWS(デプロイ後) |
| 店舗/レジ/端末ID |
.env → scripts/use-env.ps1 |
infra/template.yaml のパラメータ |
| BEの環境変数 |
infra/env.local.json(ASCIIのみ) |
template.yaml の Globals / パラメータ |
| AWS認証情報/MFA |
~/.aws/config(.env はプロファイル名のみ) |
同左(デプロイ端末) |
| アプリ機能フラグ |
config/{env}.json と features.* |
SSM /{env}/shoptools/config/flags |
| シークレット |
.env(ローカルのみ) |
SSM SecureString |
| WSエンドポイント |
WS_MODE=local / /ws |
SSM /{env}/shoptools/ws/endpoint |
7. 検証
npm run verify
npm run test:e2e(sam)
sam validate --lint / sam build
npm run dev → npm run stop
- スマホ実機で POS→決済→完了
8. 第3弾 P10〜P14(カタログ・在庫・注文の整理)
| Phase |
内容 |
| P10 |
lib/catalog.ts にカタログ読込・検索・可用数付与を集約。bootstrap と注文価格解決を mock 固定から snapshot 参照へ変更 |
| P11 |
core/orderIntake.ts に「冪等→引当→作成→失敗時解放」を集約。顧客/店員の注文作成を共通化 |
| P12 |
repo/inventory.ts(在庫CRUD)と repo/reservations.ts(引当/解放/掃除)に分割。sync/scheduler.ts と api/routes/index.ts を追加 |
| P13 |
顧客 lib/cartDraft.ts、店員 lib/catalogStore.svelte.ts を抽出。同期DTOを api-types へ移動 |
| P14 |
CatalogItem / Availability を api-types の正とし、core は type re-export に変更 |
追加された構成
server/src/
lib/catalog.ts # loadCatalog / filterCatalog / toCustomerItems / toStaffItems
lib/syncStatus.ts # staleSecondsOf
core/orderIntake.ts # createReservedOrder
repo/reservations.ts # reserve/release/getReserve/cleanup
sync/scheduler.ts # runScheduledSync
api/routes/index.ts # ルート表
apps/customer/src/lib/cartDraft.ts
apps/staff/src/lib/catalogStore.svelte.ts
挙動の変更
/api/bootstrap と注文価格は snapshot(Square同期結果) を参照。snapshot未作成のローカルのみ mock にフォールバック
- 引当競合(楽観ロック)は1回だけ再試行
- 期限切れ引当の掃除は同期の有無にかかわらず実行
9. 第4弾 P15〜P19(同期・adapters・ツール・店員状態の整理)
| Phase |
内容 |
| P15 |
lib/syncStatus.ts に toSyncStatus / isSyncDue / cooldownRemainingMs を集約。syncStatusHandler の再帰呼び出しを廃止。readBody() を共通化 |
| P16 |
adapters の旧API(listCatalog / findCatalogItem / MockTerminalAdapter)を削除。注文価格の解決を lib/catalog.ts 経由に統一 |
| P17 |
Square同期を sync/mock.ts / sync/square/(client・mappers・types)へ分割。API呼び出しに10秒タイムアウト |
| P18 |
tools/shared/env.mjs に .env パーサーを集約。launcher はシェル優先、local-api は従来の上書き順 |
| P19 |
店員の注文・決済・ポーリングを lib/orderSession.svelte.ts へ抽出。StaffSessionStore はログイン/POS/同期/フィードに集中 |
構成
server/src/
lib/syncStatus.ts # toSyncStatus / isSyncDue / cooldownRemainingMs
sync/mock.ts
sync/square/{client,mappers,types,index}.ts
apps/staff/src/lib/orderSession.svelte.ts
tools/shared/env.mjs
挙動の変更
SyncStatus.catalogVersion は string | null
SyncRunResult.mode は "mock" | "square"
- Square API呼び出しは10秒でタイムアウト
- 店員UIの公開プロパティ(
selected / blockingOrderId)はgetterで維持