아키텍처 기준선 (as-built + as-specified)
이 페이지는 모든 요구사항이 전제하는 아키텍처를 고정한다. techspec의 설계 의도(as-specified)와 server 레포의 실제 구현(as-built)을 한 화면에서 대조해, FR/NFR이 어떤 엣지 프리미티브 위에서 측정·검증되는지를 명확히 한다. 시스템 컨텍스트·컨테이너 카탈로그·Cloudflare 프리미티브 매핑·3대 데이터 흐름·실시간 전송 결정(ADR-0010)·ADR 색인·환경 토폴로지·구현 완성도 맵을 포함한다. 수치는 모두 마스터 스펙 §4 정규표에서 인용한 진입 단계 초기 확정 목표이며 운영 베이스라인으로 분기 조정한다(달성치 아님).
server 레포의 구현 상태(DONE/STUB/ABSENT)와 한 줄씩 맞춘다.
규범적 요구사항 ID는 02-requirements 계열 페이지가 소유한다 — 이 페이지는 인프라 불변식(IR-INFRA-*) 두 건만 소유한다.
1. 시스템 컨텍스트 요약 (C4 Level 1)
LogiNippon은 일본 국내 B2B 트럭 화물에 화물 단위 실시간 가시성을 제공하는 화주(荷主)向 SaaS다. 어려움의 본질은 UI가 아니라 多重下請け(元請→下請→孫請)를 관통한 위치 확보이며(Research H3 강검증), 진입 쐐기는 규제를 킬러앱으로 삼는 것이다. 아래는 techspec overview.md C4 Level 1 다이어그램의 행위자·외부시스템을 TRD 기준선으로 표로 고정한 것이다.
| 구분 | 대상 | 시스템과의 상호작용 | 라벨 |
|---|---|---|---|
| 행위자 | 드라이버(運送会社 소속) | 案件 수신 · 위치 자동 공유 · 완료 보고(증빙사진) | 설계 |
| 화주(荷主, 대기업 물류팀) | 화물 가시성 · 荷待ち 리포트 · 규제 출력 소비 | 설계 | |
| 운송사 운영자(運送会社) | 차량 현황 · 배차 · 데이터 공유 제어 | 설계 | |
| 최종 고객(受取人) | 배달 예정 추적 링크 · 완료 알림 수신(위치 비노출, ETA만) | 확인 | |
| 외부 시스템 | 화주 TMS / WMS / ERP | REST API · 웹훅 · EDI 214(Phase 2) 양방향 | 설계 |
| LINE / LINE Works · 이메일 | 예외/완료 알림 발신 채널 | 확인 | |
| Google Maps · 国土地理院 | 경로 · 거리/교통 · 지도 타일(ETA 입력) | 확인 | |
| 気象庁(JMA) 날씨 API | ETA 날씨 피처 | 확인 | |
| OEM 텔레매틱스 / 通信型デジタコ | 위치 인입(Phase 2+, 현재 점선 의존성) | 추정 |
2. 컨테이너 카탈로그 (엣지 프리미티브 — C4 Level 2)
운영 서버 0대이므로 "컨테이너"는 배포 가능한 Cloudflare 엣지 프리미티브다. 각 단위의 용도와 진실 공급원 관계를 고정한다(techspec
cloudflare-stack.md, server/src/index.ts의 { fetch, queue, scheduled } + DO export).
| 분류 | 컨테이너 | 용도 | 진실 공급원 / 비고 |
|---|---|---|---|
| 컴퓨트(Workers·Hono) | API Worker | REST /v1 · 인증 · 라우팅(routes/{auth,jobs,shipments,reports,webhooks}.ts) | 요청은 stateless, 데이터는 D1/KV/R2 |
| Ingest Worker | 위치 배치 수신 POST /v1/ingest/positions → 검증 → DO push → Queue enqueue(ingest/positions.ts) | 응답 빠름, 무거운 처리 분리 | |
| Queue Consumer Worker | 지오펜스 평가 · 정규화 · dedup · ETA · 예외(consumers/queue.ts) | at-least-once → 멱등 처리 | |
| Report Worker(Cron 구동) | 規制 리포트 배치 생성 → R2(consumers/cron.ts) | 일/월 스케줄 트리거 | |
| 상태(DO) | Shipment Durable Object | shipment 1건당 1 인스턴스 · 라이브 위치/상태 · WebSocket fan-out · Hibernation(do/shipment.ts) | 핫 캐시·브로드캐스터, 진실은 D1; DO 재생성 시 D1 복원 |
| 비동기(Queues) | geofence-eval | 위치 → H3 셀 멤버십(KV 역인덱스 O(1)) → enter/exit 판정 | Ingest 응답 경로에서 분리 |
notify | 예외/완료 → LINE·이메일·웹훅 발송(재시도·백오프) | max_retries=8(FR-DLV-WH-001) | |
eta-recompute | 이벤트·교통 변화 시 ETA 재계산 | 룰 기반 → ML(미래) | |
| 스케줄 | Cron Triggers | ETA 재계산 + 추적손실 스윕 · 일/월 규제 리포트 | wrangler crons 3종(§6 참조) |
| 스토리지 | D1(SQLite) | 관계형 진실 공급원: shipments·stops·events·carriers·vehicles·geofences·geofence_cell(h3_r10 인덱스) | ADR-0008 |
| KV | H3 지오펜스 역인덱스 cell→geofence_id[] · 핫 config · feature flag | ADR-0009; config는 시드됨·런타임 미read | |
| R2(오브젝트) | 배송 증빙 사진 · 규제 PDF/CSV · 원시 GPS 아카이브(egress 무료) | 바인딩 PROOF/EXPORTS/GPS_ARCHIVE |
3. Cloudflare 프리미티브 → 역할 매핑 (Kafka 회피 대비)
참조 사례(project44·FourKites)가 Kafka+Flink+Kubernetes로 피크 하루 3~5 TB를 처리하는 것은 확인된 사실이나, 우리의 진입 단계 규모와 "0대 운영" 목표에는 과하다. 같은 역할을 진입 규모에 맞는 엣지 프리미티브로 대체한다(ADR-0001).
| 참조 스택(대규모) | LogiNippon(진입 단계) | 역할 | 이행 경로 |
|---|---|---|---|
| Kafka(메시지 버스) | Queues | 비동기 처리·재시도 | 스케일 Kafka급 시 외부 스트리밍 이행(ADR-0001 기록) |
| Flink / Kafka Streams | Queue Consumer Worker | 이벤트 단위 정규화·지오펜스·ETA·예외 | |
| 스트림 상태 / 실시간 조인 | Durable Objects | shipment별 라이브 상태·WS fan-out | 활성만 DO·종료는 폴링 분할 |
| Elasticsearch / Redshift | D1(+ 후속 분석 레이어) | 검색·집계(공간은 h3_r10 GROUP BY) | 외부 웨어하우스 이행 경로(ADR-0008) |
| Kubernetes | (없음) | 엣지 서버리스 | — |
4. 3대 데이터 흐름
techspec overview.md의 시퀀스 다이어그램을 TRD 기준선으로 요약한다. 각 흐름이 어떤 NFR로 측정되는지 함께 표기한다.
| 흐름 | 경로 | 핵심 동작 | 측정 SLI |
|---|---|---|---|
| ① 실시간 경로 (위치 수집→라이브 갱신) |
App → Ingest → DO(즉시 broadcast) → Queue → Consumer → KV/D1 | Ingest가 DO에 최신 위치 push(즉시 WS broadcast) 후 클라 202 응답; 무거운 지오펜스 판정은 비동기 분리. Consumer가 latLngToCell→KV 역인덱스 O(1) 조회로 멤버십 판정, 진입/이탈 시 D1 event INSERT + DO 상태 갱신. |
accept 지연 NFR-PERF-001 p95 ≤ 200ms; WS 반영 NFR-PERF-003 p95 ≤ 1.5s |
| ② 이벤트→ETA·예외·알림 | Consumer → D1(정규화 저장) → ETA 재계산 → 예외 규칙 → notify Queue → LINE/이메일/웹훅 | canonical_code로 이벤트 저장, 잔여 구간 ETA 재계산(stop.predicted_eta), 지연·荷待ち 초과·추적 손실 규칙 평가. 예외 감지 시 심각도별 채널 + 웹훅 POST(at-least-once). |
지오펜스 lag NFR-PERF-002 p95 ≤ 5s; 웹훅 성공률 NFR-RELY-001 ≥ 99.0%/28일 |
| ③ Cron 규제 리포트 | Cron → Report Worker → D1 집계 → R2(PDF/CSV) → D1 메타 | 일/월 배치로 기간 내 stop/event 집계(荷待ち 시간·실운송 계층) → 実運送体制管理簿·荷待ち 기록 문서 생성 → R2 저장(egress 무료, 반복 다운로드 무과금) → 메타 기록. | 리포트 레이트리밋 10 req/min(NFR-SEC-RL-001); 서명URL TTL 300s(FR-RPT-RANGE-001) |
5. 실시간 전송 결정 (ADR-0010 귀결)
대시보드 실시간 전송은 뷰 단위로 분리한다. 단일-shipment 상세는 즉시성이 필요하고, 플릿 개요는 다수 핀을 합리적 비용으로 갱신해야 하므로 다른 전송을 쓴다(마스터 스펙 §6 ADR-0010 귀결, 아래 §6 ADR 색인에 정규 요약).
| 뷰 | 클라이언트 | 전송 | 예산 | 구현상태 |
|---|---|---|---|---|
| 단일 shipment 라이브 뷰 | Flutter(드라이버 + 선택적 최소 화주 모바일 뷰) | WebSocket(Shipment DO fan-out) | 위치 push→broadcast NFR-PERF-003 p95 ≤ 1.5s / p99 ≤ 3s | 서버 DO·WS DONE · 클라 ShipmentWsClient STUB |
| 플릿 개요(컨트롤 타워 맵) | console(Astro·Svelte·MapLibre) | 6s 폴링 GET /v1/shipments/tracking |
NFR-PERF-003b: 핀 >60s = stale 표시, >300s = drop | 서버 batch 스냅샷 DONE(스펙 미기재·console 하드의존) |
요구. 단일-shipment 라이브 뷰는 반드시 Shipment DO의 WebSocket fan-out으로 전송하고(MUST), 플릿 개요는 반드시 GET /v1/shipments/tracking 6s 폴링 경로로 전송해야 한다(MUST). 두 경로는 서로의 SLI 예산(NFR-PERF-003 vs NFR-PERF-003b)을 침범하지 않도록 분리 유지한다.
수용기준
- Given Flutter 단일 shipment 뷰가 DO에 WS 연결됨, When 위치 push 발생, Then 대시보드 broadcast가 NFR-PERF-003 p95 ≤ 1.5s 예산 내.
- Given console 플릿 맵, When 6s 폴링으로 스냅샷 수신, Then 마지막 갱신 >60s 핀은 stale로 표시되고 >300s 핀은 drop된다(NFR-PERF-003b).
6. ADR 색인 (상태 포함)
기준선이 의존하는 아키텍처 결정 레코드(ADR). techspec adr/ 디렉터리 + 레포 거버닝 ADR + ADR-0010(대시보드 해소-제안). ADR-0001~0009는 채택됨, ADR-0010은 해소-제안 상태다.
| ADR | 제목 | 상태 |
|---|---|---|
| ADR-0001 | 전면 Cloudflare 엣지 채택(운영 서버 0대) | 채택됨 |
| ADR-0002 | Workers는 TypeScript + Hono | 채택됨 |
| ADR-0003 | 화주 대시보드 = Flutter Web(→ ADR-0010이 재범위화) | 채택됨(amended) |
| ADR-0004 ~ 0006 | 데이터 모델·수집·트래킹 엔진 결정 | 채택됨 |
| ADR-0007 | ETA 룰 기반 → ML 전환 | 채택됨 |
| ADR-0008 | D1을 1차 진실 공급원으로(단일 리전 쓰기) | 채택됨 |
| ADR-0009 | H3 지오공간 인덱싱(PostGIS 회피, KV 역인덱스 O(1)) | 채택됨 |
| server: admin-provisioned | 드라이버/계정은 관리자 프로비저닝(셀프가입 없음) | 채택됨(레포 거버닝) |
| app: native-first SDUI | 드라이버 앱 네이티브 우선 + 서버주도 UI | 채택됨(레포 거버닝) |
| ADR-0010 | 대시보드 아키텍처 해소(console=컨트롤 타워, Flutter 재범위화) | 해소-제안(Proposed-Resolved) |
console 레포를
운영자/운송사 컨트롤 타워(실시간 플릿 맵·shipments·규제 리포트·프로비저닝)로 공식 채택하고,
화주는 console 내 SHIPPER_VIEWER 읽기전용 + CustomerTrack으로 커버한다.
ADR-0003의 "화주 대시보드 = Flutter Web"은 드라이버 앱 전용(+선택적 최소 화주 모바일 뷰)으로 재범위화(amend)한다.
근거: console(Astro 6.4·Svelte 5·MapLibre)은 실제 동작하는 컨트롤 타워(4탭 map/ships/reports/issue, FleetMap 6s 폴링, nimachi 리포트)인 반면
Flutter app의 대시보드 엔트리(main_dashboard.dart)는 비동작 placeholder다 —
"채택된 것은 미구현, 동작하는 것은 미채택"의 모순. 귀결: console에 거버닝 ADR+README 부여, 단일 @loginippon/contract를 console+server+Flutter로 통일,
실시간 전송 분리(§5). 오너 = 아키텍트, 마이그레이션 노트 포함. 전체 결정 트랙은 OD-001이 소유한다.
7. 환경 토폴로지
운영 서버가 없으므로 토폴로지는 서버 배치도가 아니라 엣지 프리미티브 + 환경 바인딩의 배치다. wrangler.jsonc는 환경별로 독립 바인딩을 강제한다 — dev/staging/prod 각각 별도 D1·KV·R2·Queues를 갖는다(확인 as-built: loginippon / loginippon-staging / loginippon-prod). 일본 국내 서비스라 단일 리전 쓰기로 충분하며(읽기는 엣지 복제), 글로벌 쓰기 분산이 필요해지면 재검토한다(ADR-0008).
| 환경 | D1 | R2 버킷 | 승격 규칙 |
|---|---|---|---|
| dev | 로컬 / loginippon | loginippon-proof 등 | 로컬 workerd + 로컬 D1/KV/R2/Queues |
| staging | loginippon-staging | loginippon-proof-staging 등 | CI 통과 후 자동 |
| prod | loginippon-prod | loginippon-proof-prod 등 | 승인 게이트 + 에러버짓 미소진(NFR-AVAIL-001 0.1%/월 ≈ 43분) |
요구. 각 환경(dev/staging/prod)은 반드시 자체 D1·KV·R2·Queues 바인딩을 가져야 하며(MUST), 환경 간 데이터 교차 접근은 없어야 한다. 쓰기 진실 공급원은 단일 리전 D1로 하고, 읽기는 엣지 복제로 처리한다.
수용기준
- Given
wrangler.jsoncenv 오버라이드, When staging/prod 배포, Then 각 환경의 D1/KV/R2/Queues 이름이 환경 접미사로 분리되어 있다(Inspection). - Given prod 승격 시도, When 에러버짓이 소진됨, Then 승격이 차단된다(릴리스 게이트, pipeline-ops).
wrangler D1/KV/R2 ID는 placeholder(0000…000a·STAGING_DB_ID·PROD_DB_ID)이며 미프로비저닝이다. 브랜드명 'LogiNippon'은 전 레포 가칭이다. 사전 출시 확정/리네임 태스크로 추적한다(OD-002·OD-005).
8. server 구현 완성도 맵 (as-built)
server 레포는 자체 docs/TECHSPEC.md보다 앞서 있다 — JWT(HS256)+PBKDF2 100k 인증·refresh 로테이션·jobs CRUD·테넌트 격리·지오펜스 히스테리시스 엔진 풀·ShipmentDO·荷待ち/NIMACHI CSV 리포트 end-to-end·ingest→DO→queue→R2가 ~50 테스트(11파일, skip 없음)로 DONE이다. 아래는 라우트·핵심 경로별 상태 맵으로, 추적성 매트릭스(acceptance-traceability)와 정합한다.
| 경로 / 컴포넌트 | 역할 | 상태 | 비고 |
|---|---|---|---|
routes/auth.ts · lib/auth.ts | JWT 발급·refresh 로테이션·ingest-token·스코프 | DONE | PBKDF2 100k · auth 테스트 |
routes/jobs.ts | 案件 CRUD(테넌트 스코프) | DONE | 404 격리 회귀 |
lib/geofence-engine.ts · geofence.ts · h3.ts | enter/exit/dwell 히스테리시스 엔진 풀 | DONE | FR-GEO-DEBOUNCE-001 |
do/shipment.ts | 라이브 상태 · WS Hibernation · D1 복원 | DONE | WS fan-out |
ingest/positions.ts | 위치 배치 수신 → DO → Queue | DONE | FR-INGEST-001 |
routes/reports.ts 荷待ち/NIMACHI | 荷待ち CSV 리포트 end-to-end | DONE | FR-RPT-NIMACHI-001 |
GET /v1/shipments/tracking | 배치 라이브 스냅샷(console 6s 폴링) | DONE | 스펙 미기재·console 하드의존(as-built 추가) |
handleNotifyBatch(consumers/queue.ts) | 웹훅/알림 전달 | STUB | ack-only(구독 테이블·endpoint_secret·HMAC POST·전달상태 없음) |
handleEtaBatch | ETA 재계산 | STUB | ack-only(룰 미구현; FR-ENG-ETA-001 미충족) |
sweepActive(consumers/cron.ts) | ETA 스윕 + 추적손실 sweep | STUB | 빈 구현(FR-ENG-EXC-003 미충족) |
POST /v1/shipments(routes/shipments.ts) | shipment 등록 | STUB | 500 stub(검증·INSERT TODO) |
웹훅 CRUD(routes/webhooks.ts) | 구독 등록/삭제 | STUB | 501(구독 persist·endpoint_secret TODO) |
GET /v1/reports/jitsuunso | 実運送体制管理簿 | STUB | 501(FR-RPT-JITSUUNSO-001 미충족) |
/v1/vehicles/:id/position · /v1/geofences[/:id] · /v1/analytics/carriers/:id · POST /v1/admin/drivers | 차량/지오펜스/분석/드라이버 프로비저닝 | ABSENT | 미존재 |
| 레이트리밋 | API Key당 throttle | ABSENT | 에러맵엔 RATE_LIMITED/429 존재하나 미구현(NFR-SEC-RL-001 미충족) |
consentGate() | APPI 동의 게이트 | STUB | lib에 존재하나 ingest 경로에서 미호출(APPI 갭) |
| KV CONFIG read | ETA 속도 80/30/50·tracking-loss 45min·dwell 120·flags | STUB | 시드됨·런타임 미read(dwell만 D1 geofence.dwell_threshold_min에서 읽음; OD-007) |
METRICS(loginippon_metrics Analytics Engine) | SLI 방출 | STUB | 바인딩됨·writeDataPoint TODO → SLI 미측정 |
마이그레이션 0001/0002/0003 | 스키마·인증·지오펜스 멤버십 | DONE | 웹훅 구독은 0004 신설 권고 |
근거·상호참조
- techspec: 01-architecture/overview.md (C4 Level 1/2·3 흐름·배포 토폴로지·Kafka 회피 매핑)
- techspec: 01-architecture/cloudflare-stack.md (프리미티브별 용도·근거·연구 요구 매핑)
- code: server README ·
src/index.ts·routes/*·consumers/{queue,cron}.ts·ingest/positions.ts·do/shipment.ts·wrangler.jsonc(as-built 완성도 맵) - ADR: ADR-0001·ADR-0008·ADR-0009 + 마스터 스펙 §6 ADR-0010(대시보드 해소-제안)
- Research: tracking-engine·delivery-layer (Kafka 비교·WS 푸시 패턴·H3 PIP 대체·H3 cold-start 가설)
- 내부 참조: 02-requirements/functional · nonfunctional · api-contracts · 03-quality/regulatory · acceptance-traceability · open-decisions-rfc · pipeline-ops