아키텍처 기준선 (as-built + as-specified)

00-overview/architecture-baseline.html · LogiNippon · TRD · 2026-06-13 · 신뢰도 라벨 확인/추정/설계

이 페이지는 모든 요구사항이 전제하는 아키텍처를 고정한다. techspec의 설계 의도(as-specified)와 server 레포의 실제 구현(as-built)을 한 화면에서 대조해, FR/NFR이 어떤 엣지 프리미티브 위에서 측정·검증되는지를 명확히 한다. 시스템 컨텍스트·컨테이너 카탈로그·Cloudflare 프리미티브 매핑·3대 데이터 흐름·실시간 전송 결정(ADR-0010)·ADR 색인·환경 토폴로지·구현 완성도 맵을 포함한다. 수치는 모두 마스터 스펙 §4 정규표에서 인용한 진입 단계 초기 확정 목표이며 운영 베이스라인으로 분기 조정한다(달성치 아님).

이 페이지의 역할. 아키텍처를 새로 정의하지 않는다 — techspec 01-architecture/overview.md·cloudflare-stack.md의 설계를 TRD 기준선으로 동결하고, 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 / ERPREST API · 웹훅 · EDI 214(Phase 2) 양방향설계
LINE / LINE Works · 이메일예외/완료 알림 발신 채널확인
Google Maps · 国土地理院경로 · 거리/교통 · 지도 타일(ETA 입력)확인
気象庁(JMA) 날씨 APIETA 날씨 피처확인
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 WorkerREST /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 Objectshipment 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 TriggersETA 재계산 + 추적손실 스윕 · 일/월 규제 리포트wrangler crons 3종(§6 참조)
스토리지D1(SQLite)관계형 진실 공급원: shipments·stops·events·carriers·vehicles·geofences·geofence_cell(h3_r10 인덱스)ADR-0008
KVH3 지오펜스 역인덱스 cell→geofence_id[] · 핫 config · feature flagADR-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 StreamsQueue Consumer Worker이벤트 단위 정규화·지오펜스·ETA·예외
스트림 상태 / 실시간 조인Durable Objectsshipment별 라이브 상태·WS fan-out활성만 DO·종료는 폴링 분할
Elasticsearch / RedshiftD1(+ 후속 분석 레이어)검색·집계(공간은 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 색인에 정규 요약).

≤1.5s
WS 실시간 반영 p95 (NFR-PERF-003)
6s
console 플릿 폴링 주기
>60s
핀 stale 표시 (NFR-PERF-003b)
>300s
핀 drop (NFR-PERF-003b)
클라이언트전송예산구현상태
단일 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 하드의존)
인프라 불변식 (이 페이지 소유). 실시간 전송 분리는 NFR이 측정할 수 있도록 아키텍처 계층에서 다음을 보장해야 한다.
IR-INFRA-RT-001 뷰별 실시간 전송 분리 불변식 P0Phase 1A

요구. 단일-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-0010 · techspec overview.md · 구현 server/src/do/shipment.ts DONE · GET /v1/shipments/tracking DONE · 클라 ShipmentWsClient STUB · 검증 A(아키텍처 검토) + DO WS 테스트

6. ADR 색인 (상태 포함)

기준선이 의존하는 아키텍처 결정 레코드(ADR). techspec adr/ 디렉터리 + 레포 거버닝 ADR + ADR-0010(대시보드 해소-제안). ADR-0001~0009는 채택됨, ADR-0010은 해소-제안 상태다.

ADR제목상태
ADR-0001전면 Cloudflare 엣지 채택(운영 서버 0대)채택됨
ADR-0002Workers는 TypeScript + Hono채택됨
ADR-0003화주 대시보드 = Flutter Web(→ ADR-0010이 재범위화)채택됨(amended)
ADR-0004 ~ 0006데이터 모델·수집·트래킹 엔진 결정채택됨
ADR-0007ETA 룰 기반 → ML 전환채택됨
ADR-0008D1을 1차 진실 공급원으로(단일 리전 쓰기)채택됨
ADR-0009H3 지오공간 인덱싱(PostGIS 회피, KV 역인덱스 O(1))채택됨
server: admin-provisioned드라이버/계정은 관리자 프로비저닝(셀프가입 없음)채택됨(레포 거버닝)
app: native-first SDUI드라이버 앱 네이티브 우선 + 서버주도 UI채택됨(레포 거버닝)
ADR-0010대시보드 아키텍처 해소(console=컨트롤 타워, Flutter 재범위화)해소-제안(Proposed-Resolved)
ADR-0010 정규 요약(마스터 스펙 §6 인용). Astro+Svelte 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).

환경D1R2 버킷승격 규칙
dev로컬 / loginipponloginippon-proof로컬 workerd + 로컬 D1/KV/R2/Queues
stagingloginippon-stagingloginippon-proof-stagingCI 통과 후 자동
prodloginippon-prodloginippon-proof-prod승인 게이트 + 에러버짓 미소진(NFR-AVAIL-001 0.1%/월 ≈ 43분)
IR-INFRA-ENV-001 환경 독립 바인딩 + 단일 리전 쓰기 P0Phase 1I

요구. 각 환경(dev/staging/prod)은 반드시 자체 D1·KV·R2·Queues 바인딩을 가져야 하며(MUST), 환경 간 데이터 교차 접근은 없어야 한다. 쓰기 진실 공급원은 단일 리전 D1로 하고, 읽기는 엣지 복제로 처리한다.

수용기준

  • Given wrangler.jsonc env 오버라이드, When staging/prod 배포, Then 각 환경의 D1/KV/R2/Queues 이름이 환경 접미사로 분리되어 있다(Inspection).
  • Given prod 승격 시도, When 에러버짓이 소진됨, Then 승격이 차단된다(릴리스 게이트, pipeline-ops).
근거 techspec overview.md 배포 토폴로지 · 구현 server/wrangler.jsonc(env별 바인딩) DONE · 단 모든 D1/KV/R2 ID는 placeholder·미프로비저닝(OD-005) STUB · 검증 I(설정 검토)
프로비저닝 갭(as-built). 모든 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.tsJWT 발급·refresh 로테이션·ingest-token·스코프DONEPBKDF2 100k · auth 테스트
routes/jobs.ts案件 CRUD(테넌트 스코프)DONE404 격리 회귀
lib/geofence-engine.ts · geofence.ts · h3.tsenter/exit/dwell 히스테리시스 엔진 풀DONEFR-GEO-DEBOUNCE-001
do/shipment.ts라이브 상태 · WS Hibernation · D1 복원DONEWS fan-out
ingest/positions.ts위치 배치 수신 → DO → QueueDONEFR-INGEST-001
routes/reports.ts 荷待ち/NIMACHI荷待ち CSV 리포트 end-to-endDONEFR-RPT-NIMACHI-001
GET /v1/shipments/tracking배치 라이브 스냅샷(console 6s 폴링)DONE스펙 미기재·console 하드의존(as-built 추가)
handleNotifyBatch(consumers/queue.ts)웹훅/알림 전달STUBack-only(구독 테이블·endpoint_secret·HMAC POST·전달상태 없음)
handleEtaBatchETA 재계산STUBack-only(룰 미구현; FR-ENG-ETA-001 미충족)
sweepActive(consumers/cron.ts)ETA 스윕 + 추적손실 sweepSTUB빈 구현(FR-ENG-EXC-003 미충족)
POST /v1/shipments(routes/shipments.ts)shipment 등록STUB500 stub(검증·INSERT TODO)
웹훅 CRUD(routes/webhooks.ts)구독 등록/삭제STUB501(구독 persist·endpoint_secret TODO)
GET /v1/reports/jitsuunso実運送体制管理簿STUB501(FR-RPT-JITSUUNSO-001 미충족)
/v1/vehicles/:id/position · /v1/geofences[/:id] · /v1/analytics/carriers/:id · POST /v1/admin/drivers차량/지오펜스/분석/드라이버 프로비저닝ABSENT미존재
레이트리밋API Key당 throttleABSENT에러맵엔 RATE_LIMITED/429 존재하나 미구현(NFR-SEC-RL-001 미충족)
consentGate()APPI 동의 게이트STUBlib에 존재하나 ingest 경로에서 미호출(APPI 갭)
KV CONFIG readETA 속도 80/30/50·tracking-loss 45min·dwell 120·flagsSTUB시드됨·런타임 미read(dwell만 D1 geofence.dwell_threshold_min에서 읽음; OD-007)
METRICS(loginippon_metrics Analytics Engine)SLI 방출STUB바인딩됨·writeDataPoint TODO → SLI 미측정
마이그레이션 0001/0002/0003스키마·인증·지오펜스 멤버십DONE웹훅 구독은 0004 신설 권고
규제 포맷 RR-LEGAL 플래그. 実運送体制管理簿·荷待ち 기록·구속시간 데이터의 공식 출력 포맷·법정 필드 정의는 법무 확인 미완이다. 위 리포트(NIMACHI=DONE, jitsuunso=STUB)의 컬럼·인코딩은 RR-LEGAL-001 법무 서명 플래그 대상이며 추정으로 다룬다.

근거·상호참조