Skip to content

設計上の判断 ​

このパッケージは最初のユーザーが現れる前に設計し直されたため、互換性のためではなく、それぞれの 選択をその是非だけで決めることができました。このページには各判断と、その理由、そこから導かれる ことを記録しています。新しい判断は新しい項目として追加し、覆された判断は削除せずにその旨を 明記します。

1. データこそが製品であり、コードは便利な道具にすぎない ​

判断。 公式の情報源が正であり、data/ の JSON ファイルはそこから作られる製品です。 TypeScript を一切使わなくても、Python、スプレッドシート(release には CSV の表が含まれます)、 jq から利用できます。各ファイルの構造は TypeScript の型で一度だけ定義し、そこから生成した JSON Schema として公開しています。型・スキーマ・データが食い違えば CI が失敗します。 ライブラリは型、インデックス、ヘルパーを加えるだけで、データを理解するためにライブラリの 何かが必要になることはありません。

理由。 リファレンスデータはそれを扱うコードよりも長く生き残り、ベルギーの法務データの 利用者の多くは JavaScript のプロジェクトではありません。

その結果。 すべてのファイルに JSON Schema と、出典とライセンスを記した datapackage.json の項目があります。フィールド名と値はコードの中だけでなくドキュメントでも 説明しています。

判断。 法的な意味を持つもの(裁判所の名称、canton の管轄領域、利率)はすべて公式の情報源 から一字一句そのまま取り込み、保存します。コードは整形や検索はしてもよいが、法的な事実を 組み立ててはいけません。

理由。 事実を組み立てるコードは、テストでは捕まえられない形で誤ることがあります。以前の formatCourtName が生成したフランス語の裁判所名は誤っていました(裁判所自身は Justice de paix … と名乗っているのに Tribunal de paix … としていた。 #10)。保存された値なら情報源と照合できます。

その結果。 裁判所名は各裁判所が公表しているとおりに、公表しているすべての言語で保存し、 formatCourtName は廃止しました。canton の管轄領域は法令の条文のまま保持し、その法的根拠を ELI リンクで示します。情報源がない場合はフィールドもありません。推測で埋めることは一切しません。 解析についても同様で、注記が混じった開庁時間のテキスト、市外局番のない電話番号、文章で書かれた 住所は、推測せずに除外します。

3. 識別子のルールは一つ ​

判断。 すべてのレコードをカバーする公式コードがあればそれを使い、なければ現地語の名称から 作った小文字の kebab-case を使います。裁判所は type + 地名です。一部のレコードしか カバーしない公式コード(ECLI、OVO)はフィールドにします。識別子を参照してください。

判断。 レコード同士は、相手のレコードの id を持つフィールドを通じてのみ参照し合います。 フィールド名は参照先のデータセットにちなみます(arrondissement、canton、municipality)。 ある id を別の id から組み立てることはしません。

理由。 文字列の形式に隠された関係は、どちらか一方が変わると黙って壊れます。フィールドなら スキーマ(有効な id を列挙しています)とテストで検証されます。

その結果。 裁判所、刑務所、justitiehuis / maison de justice の住所は、同じ方法(address.municipality) で自治体にリンクします。都市名ではなく、Statbel の変換表にある郵便番号を通じてリンクします (#28)。郵便番号のレコードは自治体の名称を コピーせず、自治体にリンクします。

5. 値は正規形で、表示はコードで ​

判断。 各値は一つの正規化された標準形式で保存し、人向けの整形は関数で行います。

値保存形式標準
日付2024-06-15ISO 8601
電話番号+3253820701E.164
IBANBE73679200844560ISO 13616 電子形式
開庁時間Mo-Fr 08:30-12:30,13:30-16:00OpenStreetMap opening_hours
言語nl、fr、de のキーISO 639-1

理由。 値ごとに形式を一つにすると、値を比較でき、検証でき(IBAN のチェックサムはテスト しています)、他のソフトウェア(tel: リンク、決済システム、開庁時間のパーサー)でも使えます。 表示の慣習は文脈によって変わりますが、データは変わるべきではありません。

6. すべてのレコードに来歴を ​

判断。 すべてのデータセットは datapackage.json に出典、ライセンス、照合日を記載します。 スクレイピングしたレコードはすべて、読み取ったページと、その内容を最初に確認した日を記載します (sources: [{ url, retrieved }])。

理由。 「これはどこから来たのか、どれくらい古いのか」は、法務データについて最初に問われる ことです。

7. データは保存したスナップショットから機械が作る ​

判断。 毎週ワークフローが公式の情報源を取得し、ページをスナップショットとして sources ブランチに保存し、scripts/parse のパーサーでそこからデータを作り直してテストし、変更があれば pull request を作成します。手で入力するものは何もありません。別の週次ジョブが、利率の欠落、 照合期限切れ、リンク切れの情報源をチェックします。

理由。 法務データは気づかないうちに古くなります。人は忘れますが、スケジュールは忘れません。 手入力したデータは検証もやり直しもできませんが、パーサーはテストでき、スナップショットはその日に 情報源が何と書いていたかを正確に示します。pull request によって人が必ず確認に加わるので、 main のデータがレビューなしに変わることはありません。

その結果。 スナップショットは専用のブランチに置き、パッケージの履歴が何メガバイトもの HTML で重くならないようにしています。パーサーは実際のページを切り詰めたコピーでテストします。 パーサーが位置づけられないページがあると、半分だけ正しいレコードを作る代わりにビルドを止めます。 二つの情報源(ejustice、police.be)は GitHub のサーバーからのアクセスを拒否するため、通常の マシンから取得しています(#52)。

8. 配布:ESM のみ、Node 22.12+、release 時にビルド ​

判断。 パッケージは ESM のみで、Node 22.12 以降が必要です。ビルド成果物はコミットせず、 release ワークフローがビルドして provenance つきで npm に公開します。

理由。 Node 18 と 20 はサポートが終了しています。Node 22.12 からは require() で ESM を 読み込めるので、CommonJS のビルドを別に用意しても、データの二つ目のコピー(dual-package hazard) とテスト対象が増えるだけです。コミットしたビルド成果物は古くなり、依存関係の自動更新を壊して いました。

その結果。 import と require() のどちらも動作し、Node 22.12、22、24、26 でテストして います。データセットごとに専用の entry point があるので、利率だけが必要なアプリが読み込むのは 数キロバイトで、裁判所やすべての canton の法令の条文までは読み込みません (はじめにのページでビルドから各 entry point の サイズを計測しています)。

9. データの保証を型で表す ​

判断。 id は生成された union 型、データセットは readonly、既知の id による検索は record | undefined ではなくレコードを返し、閉じた語彙(裁判所の種類、kind、連絡先の種類)は union 型です。連絡先のラベルは裁判所が公表しているとおりの自由テキストです。閉じたリストに すると、数百の書記課の名称を少数の語に対応づけることになり、それは推測にほかなりません。

理由。 誤りが本番環境ではなくコードを書いている最中に見つかり、型がデータのドキュメントにも なります。

10. 品質ゲート ​

判断。 すべての変更は次をパスしなければなりません。厳格な TypeScript (noUncheckedIndexedAccess と exactOptionalPropertyTypes を含む)、型情報を使う厳格な ルールの ESLint、Prettier、src/ のテストカバレッジ 100 %、全データの JSON Schema による検証、 整合性テスト(id、リンク、件数、期間)、パッケージ化したものに対する publint + arethetypeswrong。

理由。 リファレンス用のパッケージは、データを手でコピーするより信頼できる場合にしか、 依存する価値がありません。

11. 時間はモデルの一部 ​

判断。 レコードは有効期間(validFrom、validTo、どちらもその日を含む)を持ち、各データ セットはどの日からの状況を記述しているかを示し(territoryCoverage、および datapackage.json の temporalCoverage)、検索は任意で日付を受け取ります:getCanton(id, { on })、 getCantonsForMunicipality(refnis, { on })。カバー範囲より前の日付を渡すと RangeError を 投げます。

理由。 法的な問いは特定の日についての問いです。召喚状が送達されたときにどの裁判所が管轄 していたか、請求書の支払期日にどの利率が適用されたか。canton、自治体、裁判所は変わります。 「現在」しか知らないモデルは、過去についての問いに今日の地図で答え、しかもそれが正しく見えて しまいます。時間を後から追加すると、すべての id と検索が変わることになるため、履歴がデータに 入る前にモデルに組み込んでいます(#27)。

その結果。 利率にはすでに期間があります。日付なしで id を検索すると今日の版が返るため、 既知の id は undefined にならない型を保ちます。日付を指定すると何も見つからないことがあります。 管轄領域だけでなく、すべてのデータセットが期間を持ちます。情報源に載らなくなったスクレイピング レコードは削除せず validTo をつけて廃止扱いにする(scripts/parse/retire.ts)ので、一度公開 された id は残り続け、閉鎖された裁判所も、昨年の召喚状に記載された裁判所として引き続き参照 できます。

12. release はパッケージだけでなくデータも運ぶ ​

判断。 各 GitHub release には、データを JSON と CSV の表で、スキーマと datapackage.json とともに一つの zip として添付します。npm はデータを入手する方法の一つにすぎません。

理由。 ベルギーの法務リファレンスデータを最も必要としている人たちは、JavaScript ではなく スプレッドシートや他の言語で仕事をしています。

13. ジオメトリは第二の情報源であり、条文が優先する ​

判断。 canton の境界は、National Geographic Institute(NGI)が Gerechtelijk Wetboek / Code judiciaire の附属書をデジタル化したものを、公表されたとおりに(頂点ごとに、簡略化なしで、 座標は 1 センチメートル単位まで)取り込み、両言語の名称で canton と対応づけ、残りは明示的な 別名テーブルで対応づけています。canton の territory の条文が法律そのものであり、ジオメトリと 条文が食い違う場合は条文が優先します。ジオメトリには専用の entry point があり、パッケージの ルートからは再 export しません。

理由。 NGI のポリゴンは同じ条文の公式な解釈なので、引用も再構築もできます。ここで道路データ から境界を導き出せば、誰も検証できない第三の解釈になってしまいます。簡略化すると線が動き、 線が最も重要なのは canton 同士が接する場所です。100 万の座標点は他のどのデータセットよりも はるかに大きいので、誰もうっかり読み込むべきではありません。

その結果。 findCantonsAt(lon, lat) はジオメトリから答え、ドキュメントにもそう明記して います。境界線の近くの点は条文で判断すべきケースです。境界のない canton や、どの canton にも 対応しない feature があると、週次ビルドが失敗します (#29)。

未決定 ​

問いIssue
2025 年 1 月 1 日より前の履歴#27
期間にわたる利息の計算(日数の数え方、複利)判例または公式のガイダンスで方法を裏づけられるまでは予定なし

コードは MIT、データは CC BY 4.0 で提供。リファレンスデータであり、法的助言ではありません。公式の情報源を確認してください。