Dit document beschrijft ontwerpkeuzes die gemaakt zijn voor het ontwerpen en specificeren van de API's binnen het programma HaalCentraal
Onderstaande Design Decisions zijn een verbijzondering van paragraaf 6.1 van de API Designrules Extensions.
We benoemen altijd zo duidelijk mogelijk wat iets is.
Hoofdregel is altijd:
- propertynamen moeten zoveel mogelijk zelfverklarend zijn (lezen van de description om de betekenis te begrijpen is liefst niet nodig)
- propertynamen zijn zo kort als mogelijk om toch voldoende duidelijk en onderscheidend te zijn en niet langer dan daarvoor nodig
Voor de namen van properties wordt lowerCamelCase toegepast.
Voor de namen van componenten in het schema wordt UpperCamelCase toegepast.
Enumeratiewaarden bevatten alleen kleine letters en underscores. Geen spaties, geen speciale tekens en geen hoofdletters.
Ratio
In sommige development-omgevingen leveren hoofdletters, spaties of speciale tekens in enumeratie-waarden een probleem op met code-genereren.
Voor de namen van endpoints, url's en parameters worden alleen kleine letters gebruikt.
- Een property die 1 maal voorkomt wordt in enkelvoud benoemd. Een property die als array gedefinieerd is wordt in meervoud benoemd.
- Als een relatie 1 keer kan voorkomen (kardinaliteit 0..1 of 1..1) dan wordt de naam van de resource in enkelvoud opgenomen; als de relatie meer dan 1 keer kan voorkomen (gedefinieerd als array), dan wordt de naam van de resource in meervoud opgenomen.
DD1.7 Bij namen van relaties (links naar gerelateerde resources) gebruiken we in principe de naam van de betreffende resource als propertynaam voor de link
-
Wanneer de gerelateerde resource één keer kan voorkomen wordt de resourcenaam omgezet naar enkelvoud. Wanneer de relatie meerdere keren kan voorkomen, wordt de resourcenaam in meervoud gebruikt.
-
Bij gebruik van de resourcenaam als propertynaam wordt lowerCamelCase toegepast (zie DD1.2).
-
Wanneer de resourcenaam niet voldoende beschrijvend is voor de betekenis van de relatie ("adres" is "verblijfplaats" van een persoon), of wanneer de resourcenaam niet onderscheidend is (ouders, partners en kinderen zijn allen relaties naar ingeschrevenpersonen) kan gekozen worden om bijvoorbeeld de relatienaam te gebruiken, of bijvoorbeeld de relatienaam gevolgd door de resourcenaam. Zo nodig wordt dit ingekort of aangepast om tot een bondige en duidelijke naam te komen.
Bijvoorbeeld:
-
woonplaats (resource "woonplaatsen")
-
openbareRuimte (resource "openbareruimten")
-
verblijfplaats (resource "adressen")
-
kinderen (resource "ingeschrevenpersonen" en relatie "heeft kinderen")
-
DD1.8 Namen van Identificatie properties zijn afhankelijk van het wel of niet voorkomen van sibling properties
Wanneer een relatie-property (niet link of embedded) alleen de identificatie van een gerelateerde resource bevat en geen andere properties, wordt als naam van de property de naam van de resource plus het woord 'Identificatie' gebruikt.
Bijvoorbeeld: maakt deel uit van + pand + Identificatie = pandIdentificatie
Indien een parameter een element betreft dat geen onderdeel van de op te vragen resource is, maar onderdeel van een gerelateerde resource, een subresource of een gegevensgroep, dan wordt de elementnaam voorafgegaan door de betreffende resourcenaam of gegevensgroepnaam en vervolgens twee underscores.
Bijvoorbeeld:
-
ingeschrevenpersoon__burgerservicenummer
-
verblijfplaats__postcode
Er wordt naar gestreefd om enumeratiewaarden te ontdoen van spaties en bijzondere tekens. Indien mogelijk worden spaties in enumeratiewaarden vervangen door underscores.
Schema componenten voor dynamische domeinwaarden (referentielijsten zoals "Tabel 32 Nationaliteitentabel") en enumeraties krijgen respectievelijk extensie "_tabel" en "_enum".
De benaming van componenten en properties voor gegevensgroepen die gebaseerd zijn op een informatiemodel worden waar nodig ingekort.
Bijvoorbeeld verblijfstitelIngeschrevenNatuurlijkPersoon wordt "verblijfstitel, overlijdenIngeschrevenNatuurlijkPersoon wordt overlijden, geboorteIngeschrevenNatuurlijkPersoon wordt geboorte, enz.
Ratio
- De namen zijn erg lang. Dit is niet bevorderlijk voor eenvoud van implementatie.
- Extensie "IngeschrevenNatuurlijkPersoon" is redundant, want het is al duidelijk dat het gaat over eigenschappen van een ingeschreven natuurlijk persoon.
DD2.1 Dynamische domeinwaarden worden in de response opgenomen met zowel de code als de omschrijving
Voor een element van een referentielijst-type, wordt in de response zowel de code als de omschrijving opgenomen. Dit betreft dynamische lijsten (tabellen) met een code en waarde, zoals "Tabel 32 Nationaliteitentabel" en "Tabel 36 Voorvoegselstabel".
Ratio
Garanderen dat verschillende systemen binnen en buiten de gemeente dezelfde (toestand) van de referentielijst kennen is duur, ingewikkeld en foutgevoelig.
Kanttekening
Als landelijk beheerde dynamische domeinwaarden ook daadwerkelijk landelijk beschikbaar gesteld worden (zoals de common ground gedachte wel beoogd) dan worden deze als resource ontsloten en dus als link (uri) opgenomen.
Voor een query-parameter waarin een entry uit een waardelijst of een landelijke tabel als selectie-criterium wordt gebruikt wordt altijd de code van de entry gebruikt.
In de API specificaties worden enumeratiewaarden opgenomen met de waarde, in de description wordt de bijbehorende code genoemd.
Ratio
Door direct de waarde in de enumeratie aan te bieden hoeft de consumer-developer niet eerst de enumeratie-code te vertalen naar de enumeratiewaarde.
Kanttekening
De lengte van de enumeratiewaarden zal zoveel mogelijk beperkt moeten worden.
Er zijn grofweg twee categoriën Hal-links waar we gebruik van maken. Links naar resources binnen het eigen domein en links naar resources die in een ander domein beheerd worden. Om discoverability te bereiken, worden voor beide categorieën de Hal-link opgenomen naar de gerelateerde resource.
Bijvoorbeeld in de resource van een ingeschreven natuurlijk persoon bij de BRP-bevraging kunnen de relaties partner(schap), ouders en kinderen embed worden opgenomen met gebruik van de expand-parameter. Dit betekent dat het mogelijk is in één aanroep de ingeschreven persoon te krijgen, met daarbij gegevens over de relaties met eventuele partner(s), ouders en kinderen.
Wanneer een gerelateerde resource expand wordt, wordt de gehele sub-resource teruggegeven, tenzij in de expand parameter alleen een deel van de gerelateerde resource gevraagd is.
Gegevens uit een andere bron/registratie (bijvoorbeeld het BAG-adres van een persoon) kunnen niet embed worden. Deze worden alleen als link opgenomen.
Ratio
We willen "tight coupling" met andere bronnen voorkomen. Over de domeinen heen wordt alleen met links verwezen.
Vanuit andere registraties bestaan er relaties naar ingeschreven natuurlijk personen. Een persoon kan bijvoorbeeld zakelijk gerechtigde zijn van een Kadastraal object of functionaris zijn van een bedrijf. Deze inverse relaties uit een andere bron worden niet opgenomen bij de ingeschreven natuurlijk persoon. Wanneer er functionele behoefte is aan deze gegevens moeten deze bij de betreffende bron (bijvoorbeeld BRK of HR) worden opgevraagd.
Ratio
We bevragen gegevens bij de bron. Een bron kan alleen gegevens leveren die ze zelf heeft.
Voor het gebruik van de API is het gebruiken van expand=true om alle relaties embed te krijgen niet toegestaan.
Ratio
Het embedded van gerelateerde resources moet bewust worden gebruikt.
*JB: Deze is vroeg in het traject opgenomen vanwege de toenmalige versie van de API-strategie. Aangezien wij maximaal 1 niveau diep embedden zal expand=tru geen enkel probleem opleveren. Ik stel voor deze design decision te laten vallen. *
Door het gebruik van de parameter expand kunnen gerelateerde resources worden embed in het antwoord. Er is besloten dat relaties van de gerelateerde resource alleen als link worden opgenomen, deze worden zelf dus niet embed.
Bijvoorbeeld van een persoon kunnen de gegevens van de gerelateerde sub-resource huwelijk/geregistreerd partnerschap direct worden meegeladen. Daarin zitten alleen de gegevens van de relatie zoals die ook in betreffende categorie van LO GBA voorkomen, zoals ook de naam van de partner. Wanneer echter de partner (ook) een ingeschreven persoon is, wordt alleen een hyperlink naar de resource van deze persoon opgenomen. De gegevens van deze ingeschreven persoon (de partner) mogen niet ook worden embed.
Ratio
Implementatie en gebruik eenvoudig houden. Er is geen functionele behoefte om diep gegevens te embedden. Het opvragen van relaties is eenvoudig. Bij dieper embedden kan doelbinding een probleem worden. Bij dieper embedden kunnen er aan de provider-kant performanceproblemen ontstaan.
DD3.6 De identificatie van de gerelateerde resources worden opgenomen in de content van de opgevraagde resource
Voor developers die geen HAL links willen gebruiken wordt tevens de identificatie van de gerelateerde resource opgenomen in de content van de opgevraagde resource.
Historie wordt aflopend gesorteerd op datum geldigheid (datumVan).
Historie elementen krijgen de namen 'beginGeldigheid' en 'eindGeldigheid'. Binnen de NEN3610 standaard is er een keuze-mogelijkheid (datum en datumtijd) voor het formaat waarin de historie wordt bijgehouden. De Bron houdt geen tijd bij dus schrijven we het datum-formaat voor.
Binnen de historie-endpoints wordt alleen de actuele situatie met betrekking tot "in Onderzoek" getoond. Er wordt geen historie getoond van de onderzoeken die in het verleden hebben plaatsgevonden.
In OAS3 wordt er een warning gegeven als er binnen een object een Description wordt opgenomen naast een $ref (Geen enkele sibling is geoorloofd). In de draft van OAS3.1 is een description binnen een object naast een $ref wel geaccepteerd. Vooruitlopend op deze versie is het binnen de HaalCentraal-specificaties geaccepteerd dat er binnen een object naast een $ref een Description wordt opgenomen en wordt de warning voor lief genomen. Zie hier voor een toelichting.
Bij hergebruik van Yaml-componenten wordt gebruik gemaakt van absolute links zodat de openapi.yaml in alle editors onderhoudbaar is en dus iedereen die een bijdrage wil leveren een pull-request kan indienen.
Ratio
We willen bijdragers tijdens het ontwikkeltraject niet dwingen om de file-structuuur van de provider-developer over te nemen. Dit leidt tot onnodige complexiteit op het ontkoppelpunt van provider en consumer tijdens het ontwikkeltraject.
Kanttekening
Nu verwijzen de links nog naar de master-branch van Haal-centraal-common. Op het moment dat de specificaties van 1 van de API's definitief wordt zal ook de common geversioneerd moeten worden (d.m.v. een release op github)
Bij een specifieke bevragingen-API is het toepassen van Patterns beperkt tot de parameters.
Alhoewel de oneOf constructie een valide OAS3 constructie is levert deze bij het genereren van code problemen op. Deze constructie wordt ontweken door twee mogelijke alternatieven.
- De subtypes worden samengevoegd tot 1 object en er wordt een typeveld toegevoegd om te duiden welk subtype het betreft. Deze keuze is logisch als de subtypes grotendeels overlappen.
- De subtypes worden volledig opgenomen als property van een object. In het response is altijd maar 1 van deze properties gevuld. Deze keuze is logisch als de subtypes weing gemeenschappelijke properties hebben.
Ratio
Diverse code-generatoren gaan dus niet goed om met deze constructie en genereren foutieve code.
Kanttekening
Mochten code-generatoren in de toekomst wel goed met deze constructie om kunnen gaan dan is het het overwegen waard om deze constructie aan te passen bij de eerstvolgende major (breaking) change.
In de response worden alleen gegevens opgenomen die in de basisregistratie worden vastgelegd.
Ratio
Deze gegevens worden niet vastgelegd in een (voor alle gemeenten geldend) bronsysteem dat voor de bevraging geraadpleegd kan worden. Deze gegevens zijn dus (voorlopig) niet raadpleegbaar. Ook worden deze gegevens niet in alle gemeenten (op dezelfde manier) gebruikt.
De API levert alleen gegevens terug waar de vragende organisatie voor geautoriseerd is. Er worden geen aparte endpoints of resources gedefinieerd per autorisatieprofiel.
De bronhouder die gegevens ter beschikking stelt is verantwoordelijk voor het leveren van alleen gegevens waarvoor de juiste autorisatie bestaat. Bij de bronhouder gaat het om autorisatie op niveau van de vragende organisatie (gemeente). De gemeente is er verantwoordelijk voor de resultaten van de API aanvraag te filteren op autorisatie van de betreffende gebruiker.
Ratio
Uitgangspunt in de architectuur is gedelegeerde autorisatie.
Alle properties in de response worden in de Open API Specificaties gedefinieerd als optioneel, ook wanneer de betreffende attributen in het informatiemodel verplicht zijn.
Ratio
De hoeveelheid businesslogica in interface beperken. Zorgen dat zoveel mogelijk antwoord gegeven kan worden, ook wanneer een verwachte property geen waarde heeft. Het alternatief, het opnemen van de reden van geen waarde (zoals StUF:noValue) is dan niet nodig, wat het gebruik van de API eenvoudiger maakt. Tevens compliceert het verplicht maken van elementen de toepassing van de "fields" en de "expand" parameters en het filteren van de terug te geven gegevens op autorisatie van de organisatie.
Bijvoorbeeld ingeschreven natuurlijk persoon wordt in de schema's platgeslagen met bovenliggende types (subject, persoon, natuurlijk persoon).
Ratio
Zolang we niets doen met de abstracte types (subject, persoon, natuurlijk persoon), heeft het geen zin dit mee te nemen in de component schema's.
Kanttekening
Deze design decision is al lang geleden gemaakt. Meer recent hebben we uit technische hergebruik overwegingen abstracte types geïntroduceerd. Het is het overwegen waard om die technische keuzes te vergelijken met de keuzes uit de informatiemodellen en te controleren of daar uniformering mogelijk en wenselijk is.
De API standaard schrijft niet voor hoe zoekresultaten in de API moeten worden gesorteerd. Wanneer de client behoefte heeft aan gesorteerde resultaten, moet zij de ontvangen resultaten zelf sorteren. Dit betekent dat er in de berichtspecificaties geen gebruik gemaakt wordt van de 'sorteer' parameter.
DD5.10 Indicatoren die gebruik maken van Booleans worden alleen geretourneerd als de waarde 'true' is
In diverse situaties worden booleans opgenomen als er sprake is van indicatoren. Deze booleans worden alleen geretourneerd als de waarde van de boolean ook informatief is. De indicator wordt dus alleen opgenomen als de waarde van de Boolean 'true' is.