Ідентифікатори
Кожен запис має id. На нього посилаються інші записи, ваша база даних і ваші URL, тож id має бути правильним з першого разу, а потім ніколи не змінюватися. Ця сторінка викладає правила, пояснює, навіщо існує кожне з них, і що відбувається, коли закон змінює те, що id називає.
Правила
- Кожен запис має поле
id, унікальне в межах свого набору даних. Узагальнений код (індекс, кеш, форма) обробляє всі набори даних однаково. - Офіційний код має перевагу, якщо він охоплює всі записи. Якщо органи влади присвоюють код кожному запису набору даних, id і є цим кодом: громади використовують свій код REFNIS від Statbel (
44021для Gent). Код, що охоплює лише частину записів, натомість зберігається як поле: суди мають свої коди ECLI вecli, фламандські justitiehuizen — свій номер OVO вovo(див. чому). - Інакше — ASCII у kebab-case нижнього регістру:
^[a-z0-9]+(-[a-z0-9]+)*$, побудований за допомогоюscripts/id.ts: діакритику відкинуто, а все інше, що не є літерою чи цифрою, стає одним дефісом.Merelbeke-Melle→merelbeke-melle,Braine-l'Alleud→braine-l-alleud,Liège→liege. - З офіційної назви місцевою мовою: нідерландською у Фландрії, французькою у Валлонії, німецькою в районі Eupen. Брюссель офіційно двомовний і потребує одного фіксованого вибору: нідерландська.
- Номер — це суфікс: місця з кількома кантонами отримують
gent-1…gent-5. - Суди — це
type+ місце:justice-of-the-peace-gent-1,court-of-appeal-liege. Vredegerecht / justice de paix бере id свого кантону. Два суди одного типу в Брюсселі, по одному на кожну мову, додають-nlабо-fr(police-court-brussel-fr). Відділення чи місце засідань додає своє місце до id свого суду (enterprise-court-gent-brugge). Префікс зберігає унікальність id, коли в Gent є vredegerecht, ondernemingsrechtbank і arbeidsrechtbank, і він точно збігається зtypeзапису, тож другий словник ніколи не потрібен. - В'язниці та justitiehuizen / maisons de justice — це їхня назва місцевою мовою, без артиклів:
gevangenis-gent,prison-lantin,justitiehuis-gent,maison-justice-arlon. Їхні назви вже кажуть, чим вони є, а дві установи в одному місті (центральна і допоміжна в'язниці Leuven) різняться назвою. - Зв'язки — це поля, а не похідні рядки. Vredegerecht має поле
canton; кантон перелічує{ municipality }; судовий округ маєjudicialArea. Кожне з них містить id запису, на який вказує, а поле назване за набором даних, на який воно посилається.
Усе це забезпечують тести (test/ids.test.ts) і JSON Schemas, які перелічують допустимі id, тож висяче посилання не проходить валідацію.
Id
| Набір даних | Приклади | Правило |
|---|---|---|
| Апеляційні округи | antwerpen, brussel, gent, liege, mons | Місцева назва |
| Судові округи | oost-vlaanderen, brabant-wallon, eupen | Місцева назва |
| Кантони | gent-1, merelbeke-melle, wavre-1, oudergem, eupen-sankt-vith-1 | Місцева назва + номер |
| Межі кантонів | gent-1: одна межа на кантон, з id кантону, і canton як зв'язок | Id кантону |
| Громади | 44021, 21004 | Код REFNIS |
| Провінції | 10000, 40000 | Код REFNIS |
| Регіони | 02000, 03000, 04000 | Код REFNIS |
| Поштові індекси | 9000-44021, 1040-21005 | Поштовий індекс + код REFNIS |
| Суди | justice-of-the-peace-gent-1, enterprise-court-gent-brugge, police-court-brussel-fr, police-court-eupen-sankt-vith | Тип + місце |
| В'язниці | gevangenis-gent, prison-lantin, detentiehuis-kortrijk | Місцева назва |
| Justitiehuizen / maisons de justice | justitiehuis-gent, maison-justice-arlon | Місцева назва |
| Процентні ставки | legal, commercial | Фіксовані ключі |
Чому саме такий вибір
Чому kebab-case? Id потрапляють в URL (/cantons/gent-1), імена файлів, CSS-класи, ключі баз даних і рядки журналів. Нижній регістр уникає колізій у системах, нечутливих до регістру (файлові системи macOS і Windows, багато collation у базах даних): Gent1 і gent1 були б двома id у даних і одним файлом на диску. Дефіси зберігають межі слів читабельними без великих літер, а набір символів ніде не потребує екранування.
Чому читабельні id, а не числа чи UUID? Довідковий набір даних читають люди: justice-of-the-peace-gent-1 у рядку бази даних чи в повідомленні про помилку одразу каже, що це. Id так само стабільні, як були б числа, бо змінюються лише тоді, коли закон змінює те, що вони називають (див. нижче), і тоді новий id — правильний результат.
Чому місцевою мовою, а не англійською? Англійські назви існують для кількох місць (Ghent, Antwerp, Liege), але не для 162 кантонів, тож англійські id означали б два правила. Місцева назва — це ще й та, якою користується сама установа, і саме вона стоїть в офіційних текстах.
Чому офіційні коди? REFNIS використовують Statbel, регіони і майже кожен бельгійський набір даних. Коли код є id, поєднати будь-який із них із кантонами й судами можна без таблиці відповідності.
Чому два коди для поштового індексу? Сам поштовий індекс не визначає запис: 1040 належить до Brussel і до Etterbeek, і кожна пара — окремий запис зі своїм періодом чинності. Пара офіційних кодів, 1040-21005, унікальна і залишається незмінною, доки таблиця Statbel не змінить пару, а саме тоді новий запис і є правильним. Громада також є полем (municipality), як вимагає правило 8: id — це ключ, а не те, що слід розбирати на частини.
Чому type в id судів? В одному місці є кілька судів, і типів судів ставатиме більше (politierechtbanken, arbeidsrechtbanken, rechtbanken van eerste aanleg…). Власні поля суду кажуть, що він таке; id лише має бути унікальним і впізнаваним, і тип забезпечує і те, і те.
Чому не офіційні коди судів? Суди мають два офіційні коди, і жоден не охоплює всіх судів. Коди ECLI (HBGNT для hof van beroep у Gent) існують для кожного суду і кожної мови, але не для відділень, і в опублікованих рішеннях vredegerechten / justices de paix їх немає зовсім. Номери підприємств (KBO) належать FOD Justitie / SPF Justice (федеральному міністерству юстиції); його виробничі одиниці (establishment units) прив'язані до будівель, а не до судів, і змінюються, коли суд переїжджає. Правило, що діє для кожного запису, краще за два правила, тож коди ECLI — це поле (ecli), а id підпорядковується правилу 6.
Чому явні зв'язки? Стара схема будувала id суду, склеюючи Vredegerecht та id кантону. Так зв'язок ховається у форматі рядка, який ламається, щойно один суд обслуговує два кантони або змінюється назва. Поле canton перевіряють схема і тести, і ним можна йти в обох напрямках.
У TypeScript
Кожен набір даних має згенерований тип id (CantonId, CourtId, MunicipalityId, ProvinceId, RegionId, ArrondissementId, JudicialAreaId, PrisonId, HouseOfJusticeId, PostalCodeId), який перегенеровується з даних командою pnpm build:ids:
import { getCanton, type CantonId } from 'belgica'
getCanton('gent-1') // Canton: відомий id завжди знаходить свій запис
getCanton(userInput) // Canton | undefined: будь-який інший рядок може й не знайти
const id: CantonId = 'gent1' // помилка компіляції: це не id кантонуВикористовуйте типи id для значень, які ви зберігаєте, щоб друкарську помилку чи видалений id було виявлено під час оновлення.
Коли закон змінює id
Кантони й громади справді змінюються: громади об'єднувалися у 2019 і 2025 роках, а закони перекроюють кантони. Записи мають період чинності (validFrom, validTo), тож зміна додає історію, а не стирає її:
- Старий запис залишається, з
validTo; новий отримує новий id іvalidFrom. Id ніколи не використовується повторно для іншої речі, тож id, збережений роки тому, і далі знаходить те, що він означав. - Без дня пошук відповідає на сьогодні: пошук за id дає версію, яка діє сьогодні (для запису, що припинився, — останню), а списки, як-от
getCantonsForMunicipality, — лише те, що діє сьогодні. З{ on }обидва відповідають на той день. - Changelog перелічує кожен новий id і id, які він замінює.
Сама лише зміна назви, наприклад виправлення написання, не змінює id. Історії до 1 січня 2025 року в даних поки немає (#27): пошук на раніший день викидає RangeError, а не відповідає сьогоднішньою картою.
Id непрозорі
Id — це ключ, а не формат. court-of-first-instance-luxembourg-marche-en-famenne неможливо розкласти назад на тип, місце і відділення, бо тип, місце і мовний суфікс використовують той самий дефіс. Не розбирайте id: кожна його частина також є полем (type, court, canton, arrondissement), і саме поля перевіряють схеми й тести.
Стабільність
Id — це публічний API. Додавання записів — minor-версія, виправлення значення — patch, а видалення чи перейменування id — major-версія. Версія 0.3.0 замінює попередні id у PascalCase (VredegerechtGent1, EastFlanders) цією схемою ще до того, як пакет мав користувачів; changelog містить відповідність.
Цю обіцянку перевіряють, а не просто дають. ids.lock.json перелічує кожен id, який пакет коли-небудь опублікував, для кожного набору даних; pnpm generate додає до нього нові id і ніколи не видаляє жодного, а test/ids.test.ts падає, коли зафіксованого id немає в даних. Тож scraper, який читає сторінку з новим заголовком, перегенерований список кантонів чи ручна правка не можуть непомітно відкинути чи перейменувати id: щотижневий pull request із даними падає, і людина зіставляє нову назву з наявним id у парсері або завершує запис через validTo. Вилучення id із lock-файлу — свідомий крок, для major-версії, з id та його заміною в changelog.