Files
harukin-expo-dev-env 1ecd026908 feat: Implement official position metadata handling and caching
- Add `currentTrainMetadata.ts` to manage carrying forward official position metadata between train snapshots.
- Introduce `lineDisplayName.ts` for mapping line IDs to display names.
- Create `officialPositionCache.ts` for parsing and serializing official position data with cache validation.
- Enhance `officialPositionService.ts` to support persistent caching of official positions and improve resolver functionality.
- Update `requestCoordinator.ts` to allow cancellation of polling requests without disposing of the coordinator.
- Modify `trainTimeFiltering.ts` to incorporate line ID context when checking for duplicate train data.
- Refactor `useCurrentTrain.tsx` to support polling for current train data with official position enrichment.
- Add tests for new features, including persistent cache behavior and handling of ambiguous train data.
2026-09-15 13:48:21 +09:00

7.1 KiB
Raw Permalink Blame History

JR四国データモデル契約(素案)

Status: Phase 1.5 contract only

この文書は、既存APIレスポンスや画面処理を一括移行するものではない。駅DBとアプリの責任分界を先に固定し、将来のRepository/API整理でidentityを再び混同しないための境界である。

Physical Station

Physical Stationは、現実の一つの駅施設・旅客駅を表す概念である。物理駅ID(PhysicalStationId)はBackendの駅情報DBまたは合意済みdomain masterが発行する値を使う。

アプリは駅番号から物理駅IDを算出・発明しない。Backendから対応付けが届かない間は、contract上のphysicalStationIdをnullとして扱う。

Station Stop

Station Stop(StationStopId)は、路線・方向・運行上の文脈を持つline-specificな停車点である。同じPhysical Stationに複数のStation Stopが存在し得る。

Y00とT28が同じ高松の別路線文脈を表す場合、番号を一つの物理駅IDとして扱わず、物理駅、停車点、番号、路線を別フィールドで保持する。

Station Number

Station Numberは、路線またはデータソースが付与した表示・検索用のlegacy referenceである。M12のように複数の路線・駅で再利用される可能性があるため、単独でlong-lived canonical IDにしない。

adaptLegacyStationNumber()は、legacy numberをStationIdentityの境界へ載せるだけで、physicalStationIdを埋めない。lookupLegacyStations()は曖昧な候補を全件返し、呼び出し側が路線・Backend mappingを確認する。

実データの全駅変換、既存のStationNumber検索、WebView注入処理はこのフェーズでは変更しない。

Train Identity

列車番号はidentityの一部であり、単独のcanonical keyではない。最低限、次の値を組み合わせる。

  • trainNumber
  • serviceDate
  • lineId(不明な場合はnullだが、省略しない)
  • source(API・namespace)

必要に応じてoperatorCodeやserviceVariantを追加する。trainIdentityKey()はこれらを複合化するため、同じ列車番号でも日付、路線、sourceが違えば別keyになる。

Calendar Date

Calendar Dateは通常の暦日(YYYY-MM-DD)である。タイムゾーンを暗黙にしたJavaScript Dateをidentityの代わりに保存しない。

Service Date

Service Dateは鉄道の営業日・運行日である。アプリの表示上の暦日と一致しない場合がある。契約ヘルパーの既定境界は04:00で、00:00〜03:59のwall clockは前日のService Date、04:00以降は当日のService Dateとする。この境界はBackendとの最終合意で変更可能なpolicyであり、各処理が独自に判定してはいけない。

Wall Clock Time

Wall Clock Timeは日常の00:00〜23:59である。24時台表記をこの型へ混ぜない。parseWallClockTime()は24:00を受け付けない。

Service Minute

Service Minuteは、extended railway timeを整数分で表すpure valueである。JavaScript Dateへ直接変換しない。

00:00 = 0
23:59 = 1439
24:00 = 1440
24:30 = 1470
25:15 = 1515

parseServiceMinute()は上記のextended表記を扱う。一方、通常のwall clockを現在のService Day軸へ写像するserviceMinuteFromWallClock()では、既定04:00境界の00:30は1470になる。つまり「文字列として00:30を読む」ことと「営業日軸へ写像する」ことを分離する。

Legacy dataとの関係

既存のY00、T28、M12などの処理は直ちに変更しない。新しいAPI層やRepositoryは、legacy referenceを境界で受け取り、Backendが返すPhysicalStationId・StationStopIdとの対応をadapterで明示する。対応がないデータを推測で結合しない。

既存の運行情報・列車位置データは、今回追加した型へ一括変換せず、fixtureと境界関数から段階的に移行する。

Backendとの責任分界

Backend/domain masterが責任を持つもの:

  • Physical Stationのcanonical ID発行と同一物理駅の統合
  • Station Stop、路線、駅番号、営業日ルールの正式な対応表
  • 列車番号・Service Date・路線・sourceを含むidentity情報
  • schema version、fetchedAt、sourceの仕様

アプリが責任を持つもの:

  • contractに沿った型境界とlegacy adapter
  • 欠損・曖昧なidentityを推測でcanonical化しないこと
  • Service MinuteをDateへ押し込まずに表示・比較すること
  • Backend contract変更時のfixture、parser、release compatibility確認

Backendがcanonical mappingを提供するまでは、アプリのphysicalStationId: nullは未解決状態として保持する。

Official Position

OfficialPositionは、JR公式走行位置の完全一致tupleと、Backendが発行したUUIDを持つcanonical Position metadataである。アプリ側のlookup identityは、pos、line、pos_numの3値をこの順で保持する。pos_numは文字列であり、109、0279、ABCを数値化しない。

Backend responseは次のNative型へ変換する。

  • positionId / pos / line / posNum
  • positionType / platform / track / note
  • enrichmentStatus / firstSeenAt / lastSeenAt / revision

Position metadataの解決はCurrentPositionの取得・更新とは別の処理として扱う。起動時は検証済みのOfficialPosition metadataをAsyncStorageの永続ウォームキャッシュから先に返し、次回pollでAPIを再取得する。session memory cacheとin-flight dedupeを併用し、一覧表示などの多数tupleではbounded concurrencyを使う。404はunresolvedとしてmetadataだけを欠落させ、走行位置・遅延・列車番号などを失敗させない。ネットワーク障害時も同様にCurrentPositionの表示を維持する。

Resolverのアプリ向け入口は一括解決に絞り、tupleの重複除去はResolverが所有する。未使用の管理用APIを追加せず、通信のobservabilityと上記の失敗時の表示契約を維持する。 CurrentTrainのsnapshotは、同じ列車番号でもLineを持つ在線観測を行単位で保持する。路線コンテキストがある画面では駅番号・路線の対応を選択条件にし、一般ユーザー向け表示は候補を一件に絞る。管理者向け表示だけは候補を全件表示し、行ごとのOfficialPosition metadata(platform / trackを含む)を使う。路線コンテキストがないfallbackでは、複数行のどれかへ推測で結び付けず、直前のline-specificな候補を保持する。

すべてのNative環境でOfficialPosition Backend resolverをcanonical sourceとして使う。production release / production betaはproduction Backend、experimentalはbeta Backendへ環境configで切り替える。自動fallbackは行わず、旧n8n Position masterのadapter・書き込み経路はNativeから削除する。管理権限がありUUIDとuserIDが揃った場合のみ、環境対応Frontendのcanonical編集URLをgeneralWebViewで開く。