1. Inleiding en lagenmodel
Dit document beschrijft de data-architectuur van het Jottem-platform: welke data waar wordt vastgelegd, hoe die door het platform stroomt en in welke vormen die aan de buitenwereld wordt aangeboden. Het sluitstuk is de outputcontrole: per output is nagegaan of alle benodigde data beschikbaar is in de eerdere lagen; de daarbij gevonden hiaten zijn direct in het datamodel verwerkt.
De data leeft in drie lagen:
| Laag | Opslag | Inhoud |
|---|---|---|
| Bronlaag | PostgreSQL (ERD), externe Object Storage (S3), miiify | PostgreSQL is de bron van waarheid voor organisaties, gebruikers, media en metadata; de externe Object Storage (S3) bevat de mediabestanden (originelen, afgeleiden, thumbnails, audio); miiify bevat de W3C-annotaties met versiegeschiedenis. |
| Verwerkingslaag | Celery-workers, Elasticsearch, Fuseki | Workers genereren afgeleiden en thumbnails, mappen metadata naar RDF (upsert in Fuseki), indexeren in Elasticsearch en versturen e-mail. Elasticsearch en Fuseki zijn afgeleide stores: ze zijn altijd volledig te herbouwen uit de bronlaag. |
| Outputlaag | api/iiif/anno/data.iotm.nl, RSS | De standaard-interfaces voor de buitenwereld: IIIF (Image, Presentation, Change Discovery), W3C Web Annotation Protocol, RDF/SPARQL/datadump met datasetbeschrijving, de REST-API’s en RSS-feeds. |
Leidend principe (zie ook de systeemarchitectuur): standaarden aan de buitenkant - elke output volgt een open standaard zodat harvesters en aggregators (o.a. NDE) zonder maatwerk kunnen aansluiten - en PostgreSQL plus miiify en de Object Storage als bron van waarheid, zodat elke afgeleide store en elke output reproduceerbaar is.
2. ERD diagram
erDiagram
Organisatie {
int id PK
string naam
string slug
string beschrijving
string website
string NAAN
string kleurenpalet
string logo
string favicon
}
Project {
uuid projectId PK
string naam
string slug
string beschrijving
string oproep
date startDatum
date eindDatum
string afbeelding
string datasetLicentie
string terminologiebronnen
string status
int organisatieId FK
}
Gebruiker {
int gebruikersId PK
string naam
string email
bool naamPubliek
date bevestigingsDatum
date registratieDatum
date laatsteLoginDatum
bool favorietenPubliek
}
GebruikerRol {
int gebruikerRolId PK
int gebruikersId FK
int organisatieId FK
string rol
}
Media {
uuid mediaId PK
string titel
string beschrijving
string bestandsnaam
string genre
string licentie
int breedte
int hoogte
string mimeType
string status
string afkeurReden
string herkenbaar
float herkenbaarBetrouwbaarheid
bool toestemmingPersonen
string ark
date creatieDatum
date publicatieDatum
date wijzigingsDatum
int gebruikersId FK
int organisatieId FK
uuid projectId FK
}
Metadata {
int metadataId PK
uuid mediaId FK
int gebruikersId FK
string type
string label
string value
string uri
}
Favoriet {
int favorietId PK
int gebruikersId FK
uuid mediaId FK
date creatieDatum
}
Verwijderverzoek {
int verzoekId PK
uuid mediaId FK
string reden
string email
string status
string toelichting
date creatieDatum
date afhandelDatum
}
Melding {
int meldingId PK
uuid mediaId FK
string annotatieIri
string reden
string status
date creatieDatum
date afhandelDatum
}
Gebeurtenislog {
int logId PK
string type
date tijdstip
int organisatieId FK
uuid projectId FK
int gebruikersId FK
string payload
date verwerktOp
}
%% Project: campagne op organisatieniveau (bijv. "Smaak van Gouda"), beheerd door de
%% organisatiebeheerder. Elke organisatie heeft minstens een project; elke jottem hoort
%% bij precies een project (Media.projectId verplicht). Project.status: actief / afgerond.
%% Project.datasetLicentie: licentie van de projectdataset (datasetbeschrijving per project).
%% Project.terminologiebronnen: lijst van bron-URI’s uit het NDE Termennetwerk die binnen
%% dit project beschikbaar zijn voor term-URI’s (standaard: alle bronnen).
%% Publiek zichtbare identifiers (mediaId, projectId) zijn betekenisloze UUID’s; interne
%% id’s (gebruikersId e.d.) blijven interne sleutels.
%% Rollen: een gebruiker kan meerdere rollen hebben, per organisatie (GebruikerRol);
%% de rol platformbeheerder heeft geen organisatieId. Lidmaatschap van een organisatie
%% volgt uit de GebruikerRol-rijen. GebruikerRol is de LEIDENDE bron voor autorisatie;
%% de IdP (Authentik) doet uitsluitend authenticatie (koppeling via sub-claim).
%% Media.status: nieuw / goedgekeurd / afgekeurd / gedepubliceerd; afkeurReden alleen bij
%% afgekeurd. Afgekeurde jottems kunnen door de uploader worden bijgewerkt en opnieuw
%% ingediend (terug naar nieuw); gedepubliceerd volgt uit een gehonoreerd verwijderverzoek
%% (tombstone op de duurzame URL, schoning van alle outputs).
%% Verwijderverzoek.status: open / gehonoreerd / afgewezen.
%% Gebeurtenislog: bron voor statistieken (type: login, upload, goedkeuring, afkeuring,
%% annotatie, ...); persoonsgebonden regels worden na een bewaartermijn geaggregeerd of
%% geanonimiseerd (AVG).
%% Media.herkenbaar (ja/nee) + herkenbaarBetrouwbaarheid: resultaat van de Herkenbaar API
%% bij upload (paradata, geen gebruikersmetadata); toestemmingPersonen: verklaring van de
%% uploader dat toestemming van herkenbare personen is geregeld.
%% Media.titel/beschrijving/licentie, breedte/hoogte/mimeType en wijzigingsDatum zijn
%% toegevoegd n.a.v. de outputcontrole in de data-architectuur (IIIF-label/rights/canvas,
%% RDF schema:name/license, RSS en IIIF Change Discovery).
%% Organisatie.beschrijving/website: nodig voor publisher-informatie, IIIF provider en
%% RSS-channel. Gebruiker.naamPubliek: bepaalt of de naam als creator bij annotaties
%% getoond wordt.
%% Organisatie.NAAN en Media.ark zijn gereserveerd voor de ARK-fase (uitgesteld,
%% zie keuze-oplossingsrichting).
%% Locatie- en tijdlijngegevens (adres, openings-/sluitingsjaar, geo-WKT, archiefbron)
%% worden vastgelegd als Metadata-rijen op Media; er is geen apart locatiemodel.
%% De coordinaten komen van de speld die de uploader op de kaart prikt (lat/lon als
%% Metadata-rijen); er is geen geocoding-dienst in de MVP.
%% Melding: rapportage van een annotatie of reactie (spam/ongepast) door bezoekers;
%% annotatieIri wijst naar de W3C-annotatie in miiify, status: nieuw / afgehandeld /
%% afgewezen. Afhandeling door de moderator, gelogd in het Gebeurtenislog.
%% Gebeurtenislog is tevens de transactional outbox (zie systeemarchitectuur):
%% payload beschrijft de mutatie, verwerktOp markeert succesvolle doorwerking naar
%% Elasticsearch / Fuseki / Varnish-purge.
%% Relationships
Organisatie ||--o{ GebruikerRol : "kent"
Organisatie ||--o{ Media : "bevat"
Organisatie ||--o{ Project : "voert_uit"
Project ||--o{ Media : "verzamelt"
Gebruiker ||--o{ GebruikerRol : "heeft"
Gebruiker ||--o{ Media : "uploadt"
Media ||--o{ Metadata : "heeft"
Gebruiker ||--o{ Favoriet : "maakt"
Media ||--o{ Favoriet : "wordt_gefavoriet"
Media ||--o{ Verwijderverzoek : "betreft"
Media ||--o{ Melding : "betreft"
Organisatie ||--o{ Gebeurtenislog : "logt"
Gebruiker ||--o{ Metadata : "maakt"
3. URI-strategie en content negotiation
Elke gepubliceerde jottem krijgt een duurzame platform-URL die als identifier in alle outputs wordt gebruikt:
| Resource | URI-patroon |
|---|---|
| Jottem (duurzame link) | https://www.iotm.nl/jottem/{mediaId}
|
| Projectpagina (publiek) | https://www.iotm.nl/{organisatieSlug}/{projectSlug}
|
| Project (API) | https://api.iotm.nl/v1/project/{projectId}
|
| IIIF Image API | https://iiif.iotm.nl/3/{mediaId} (/info.json, tiles)
|
| IIIF Manifest | https://api.iotm.nl/v1/jottem/{mediaId}/iiif/manifest
|
| IIIF Collection | https://api.iotm.nl/v1/organisatie/{slug}/jottems/iiif/collection en https://api.iotm.nl/v1/project/{projectId}/iiif/collection
|
| Annotaties van een jottem (container) | https://anno.iotm.nl/annotations/{jottemId}/
|
| Annotatie | https://anno.iotm.nl/annotations/{jottemId}/{annotationId}
|
| Annotaties van organisatie / project | https://api.iotm.nl/v1/organisatie/{slug}/annotations en https://api.iotm.nl/v1/project/{projectId}/annotations
|
| Projectdataset | https://data.iotm.nl/project/{projectId}/dataset (beschrijving), https://data.iotm.nl/project/{projectId}/dump.nt.gz (dump), https://data.iotm.nl/sparql (endpoint)
|
Deelbaarheid. Elke jottem-detailpagina toont deelknoppen naar sociale media en de HTML-weergave bevat Open Graph-metadata, zodat gedeelde links (ook via de deel-oproep in de goedkeuringsmail) een nette preview tonen:
| Open Graph-element | Bron |
|---|---|
og:title
| Media.titel
|
og:description
| Media.beschrijving
|
og:image (+ og:image:width/height)
| afgeleide uit de Object Storage (voldoende groot voor previews, ± 1200×630) |
og:url
| de duurzame jottem-URL |
og:type, og:site_name, og:locale
| vast (article, "Jottem", nl_NL)
|
De duurzame jottem-URL ondersteunt content negotiation: Accept: text/html levert de HTML-weergave (IIIF-viewer + metadata + annotaties), application/ld+json, text/turtle of application/rdf+xml levert de RDF-representatie conform schema.org AP NDE (303-redirect naar data.iotm.nl). De mediaId en projectId zijn niet-herbruikbare, betekenisloze identifiers (UUID); duurzame data-URI’s zijn daarop gebaseerd, terwijl publiekspagina’s leesbare slugs gebruiken. Zo kan in de latere ARK-fase (zie het keuzedocument) een ARK ark:/{NAAN}/{shoulder}{mediaId} zonder linkbreuk op dezelfde resource worden gelegd.
4. IIIF
4.1. IIIF Image API v3
Cantaloupe serveert per gepubliceerde jottem info.json, tiles, regio’s en formaten op basis van de pyramidal TIFF/JP2-afgeleide in de Object Storage (derivatives-bucket). De benodigde data:
| Element | Bron |
|---|---|
id
| URI-strategie (afgeleid van Media.mediaId)
|
width, height
| Media.breedte, Media.hoogte (vastgelegd bij verwerking van de upload)
|
| beeldata (tiles) | Object Storage derivatives (pyramidal TIFF/JP2, gegenereerd door worker)
|
4.2. IIIF Presentation API v3 (Manifest en Collection)
De backend genereert per jottem een Manifest en per organisatie en per project een Collection (met iiif-prezi3), rechtstreeks uit de database:
| Manifest-element | Bron |
|---|---|
id, type
| URI-strategie |
label
| Media.titel
|
summary
| Media.beschrijving
|
metadata (paren)
| Metadata-rijen (vervaardiger, datering, plaats, steekwoorden, ...)
|
rights
| Media.licentie (URI)
|
requiredStatement (bronvermelding)
| Organisatie.naam + inzender (indien Gebruiker.naamPubliek)
|
provider
| Organisatie.naam, Organisatie.logo, Organisatie.website
|
navDate
| Metadata type datering
|
thumbnail
| Object Storage thumbs
|
canvas (width/height)
| Media.breedte, Media.hoogte
|
| image service | IIIF Image API (zie boven) |
annotations (AnnotationPage)
| miiify, per jottem-target |
| Collection-items | Media per organisatieId/projectId (status goedgekeurd)
|
Audio-jottems (fase 2, zie de uploadscope in de requirements) krijgen een manifest met een audio-canvas (duration; vastgelegd bij verwerking van de upload) en het bestand uit de audio-bucket als painting annotation.
4.3. IIIF Change Discovery v1
Per organisatie publiceert de backend een ActivityStreams-feed (OrderedCollection met pages) waarmee harvesters nieuwe en gewijzigde jottems kunnen volgen:
| Element | Bron |
|---|---|
Create-activiteit
| Media.publicatieDatum (moment van goedkeuring/publicatie)
|
Update-activiteit
| Media.wijzigingsDatum
|
object (het Manifest)
| URI-strategie |
endTime
| de betreffende datum |
Regel: elke mutatie die de outputs raakt - metadata-wijziging, nieuwe/gewijzigde/verwijderde annotatie, depublicatie - werkt Media.wijzigingsDatum bij, zodat de Change Discovery-feed (en schema:dateModified in de RDF) ook annotatie-wijzigingen omvat, zoals de usecase voor API-gebruikers vraagt. Het verwijderen van een annotatie levert daarbij een Update-activity op de jottem op (géén Delete: de jottem zelf blijft bestaan; Delete/tombstone is voorbehouden aan depublicatie van de jottem). De AnnotationCollections komen live uit miiify en tonen de verwijderde annotatie direct niet meer; de workers hersynchroniseren daarna de named graph in Fuseki en de zoekindex via het outbox-mechanisme uit de systeemarchitectuur.
5. Annotaties
Annotaties volgen het W3C Web Annotation Data Model en worden opgeslagen in miiify; lezen kan publiek en standaardconform via het W3C Web Annotation Protocol op anno.iotm.nl (de Miifi API). Schrijven verloopt via de beheer-API, die de geauthenticeerde gebruiker als creator toevoegt en autorisatie bewaakt (eigen annotaties bewerken en verwijderen; de git-backend van miiify bewaart de versiegeschiedenis).
| Element | Bron |
|---|---|
id, created, modified
| miiify |
motivation
| vocabulaire, zie § 9 Vocabulaires en termen |
target (gehele jottem)
| duurzame jottem-URL of Canvas-URI |
target (getekend vlak)
| Canvas-URI + SVG/FragmentSelector (Annotorious) |
body (vrije tekst: herinnering, aanvulling, correctie)
| TextualBody |
body (identificatie persoon/gebouw/bedrijf/plaats/gebeurtenis)
| SpecificResource met label + externe URI (zie § 9 Vocabulaires en termen) |
body (geo: standpunt fotograaf, zichtveld)
| WKT/GeoJSON-body, zie § 10 Zoekindex en geo-data |
| reactie op annotatie | annotatie met de bestaande annotatie als target
|
creator
| Gebruiker.naam indien Gebruiker.naamPubliek, anders geanonimiseerd
|
5.1. Ontsluiting: AnnotationCollection per jottem en per organisatie
De annotatieserver hanteert één container per jottem (containernaam = mediaId). Daarmee zijn annotaties op drie niveaus opvraagbaar, telkens W3C-conform:
| Niveau | Endpoint | Output |
|---|---|---|
| Individuele annotatie | https://anno.iotm.nl/annotations/{jottemId}/{annotationId} (en GET /annotation/{annotationId} in de publieke API)
| Annotation
|
| Per jottem | GET https://anno.iotm.nl/annotations/{jottemId}/ - rechtstreeks uit de container; de publieke API biedt GET /jottem/{jottemId}/annotations als doorverwijzing en het IIIF Manifest verwijst hiernaar via annotations
| AnnotationCollection (gepagineerd via AnnotationPages)
|
| Per project | GET /project/{projectId}/annotations in de publieke API - de backend aggregeert de containers van alle gepubliceerde jottems van het project (jottem-lijst uit Media.projectId)
| AnnotationCollection (gepagineerd via AnnotationPages)
|
| Per organisatie | GET /organisatie/{slug}/annotations in de publieke API - de backend aggregeert de containers van alle gepubliceerde jottems van de organisatie (jottem-lijst uit Media)
| AnnotationCollection (gepagineerd via AnnotationPages)
|
6. RDF en datadump
6.1. Mapping naar schema.org AP NDE
Een worker mapt elke gepubliceerde jottem naar RDF conform het schema.org-profiel van NDE en upsert deze in Fuseki (named graph per organisatie). De representatie is opvraagbaar via content negotiation op de duurzame URL, via SPARQL en via de datadump.
| schema.org AP NDE | Bron |
|---|---|
@id
| duurzame jottem-URL (URI-strategie) |
@type
| schema:ImageObject / schema:AudioObject op basis van Media.mimeType
|
schema:name
| Media.titel
|
schema:description
| Media.beschrijving
|
schema:genre / schema:additionalType
| Media.genre + term-URI (zie § 9 Vocabulaires en termen)
|
schema:license
| Media.licentie
|
schema:datePublished / schema:dateModified
| Media.publicatieDatum / Media.wijzigingsDatum
|
schema:dateCreated
| Metadata type datering
|
schema:creator
| Metadata type vervaardiger
|
schema:contentLocation
| Metadata type plaats/adres (schema:Place, schema:sameAs naar term-URI, schema:geo bij WKT/GeoJSON)
|
schema:temporalCoverage
| Metadata openings-/sluitingsjaar
|
schema:about
| Metadata/annotaties: personen, gebouwen, bedrijven, gebeurtenissen (label + URI)
|
schema:keywords
| Metadata type steekwoord
|
schema:isBasedOn / schema:subjectOf
| Metadata type archiefbron (label + URI)
|
schema:contentUrl, schema:width, schema:height, schema:encodingFormat
| IIIF Image-URI, Media.breedte/hoogte/mimeType
|
schema:thumbnailUrl
| Object Storage thumbs
|
schema:publisher
| Organisatie.naam + Organisatie.website
|
schema:isPartOf
| de projectdataset-IRI (Media.projectId)
|
schema:inLanguage
| vast nl (NL-only, zie niet-functionele requirements)
|
| annotaties | verwijzing naar het W3C Annotation Protocol-endpoint per jottem |
Persoonsgegevens van inzenders komen niet in de RDF: de inzender wordt alleen als schema:contributor opgenomen indien Gebruiker.naamPubliek.
6.2. Datasetbeschrijving en datadump
Per project publiceert het platform een datasetbeschrijving als schema:Dataset conform de NDE-requirements voor datasets, aanmeldbaar bij het NDE Datasetregister; de platformbrede datacatalogus (schema:DataCatalog, /datacatalog) bundelt alle projectdatasets. Omdat elke jottem bij precies één project hoort, dekken de projectdatasets samen alle gepubliceerde data.
| Verplicht/aanbevolen element | Bron |
|---|---|
| IRI van de dataset | https://data.iotm.nl/project/{projectId}/dataset
|
schema:name
| Project.naam
|
schema:description
| Project.beschrijving
|
schema:license (van de dataset)
| Project.datasetLicentie
|
schema:publisher
| Organisatie.naam, Organisatie.website
|
schema:distribution
| datadump (dump.nt.gz, application/n-triples) én SPARQL-endpoint; aanvullend zie § 6.3 Aanvullende distributies (verkenning)
|
schema:dateModified
| afgeleid: max(Media.wijzigingsDatum) per project
|
schema:temporalCoverage
| Project.startDatum – Project.eindDatum
|
schema:inLanguage
| vast nl
|
De datadump wordt per project periodiek (nachtelijks) door een worker gegenereerd uit Fuseki (named graph per project) en is tevens onderdeel van de exit-/exportstrategie uit het keuzedocument: de dumps plus de mediabestanden vormen samen een volledige, herbruikbare export van de gepubliceerde data.
Beheer en aanmelding. De organisatiebeheerder bewerkt per project de velden waaruit de datasetbeschrijving wordt gegenereerd (Project.naam, beschrijving, datasetLicentie; publisher-gegevens uit de organisatie) en kan de beschrijving - uitsluitend wanneer het project openbare (gepubliceerde) data heeft - valideren en aanmelden bij het NDE Datasetregister via de Datasetregister-API: de backend valideert eerst tegen het SHACL-shape van het register (PUT /datasets/validate), meldt daarna de URL van de datasetbeschrijving aan (POST /datasets) en kan deze ook weer afmelden (DELETE /datasets). Het domein iotm.nl moet daarvoor eenmalig op de allowlist van het register staan (POST /allowed-domains). Zie de bijbehorende endpoints in de beheer-API.
6.3. Aanvullende distributies (verkenning)
Naast de RDF-datadump en het SPARQL-endpoint kunnen ook de andere outputkanalen van een project als schema:distribution in de projectdatasetbeschrijving worden opgenomen; een distributie vereist slechts een schema:contentUrl en schema:encodingFormat, en het SHACL-shape van het register staat extra distributies toe. Dat maakt álle toegangswegen tot de projectdata vindbaar via één datasetbeschrijving:
| Kandidaat-distributie | contentUrl | encodingFormat | Overwegingen |
|---|---|---|---|
| RDF-datadump (bestaand) | https://data.iotm.nl/project/{projectId}/dump.nt.gz
| application/n-triples
| de kern-distributie voor NDE-compatibiliteit |
| SPARQL-endpoint (bestaand) | https://data.iotm.nl/sparql
| application/sparql-results+json
| bevraagbaar per named graph (project) |
| IIIF Collection | de collection per project | application/ld+json (IIIF Presentation-profile)
| direct bruikbaar in IIIF-viewers en -aggregators; bestaat in de publieke API |
| RSS | de jottem-feed per project | application/rss+xml
| laagdrempelig volgen van nieuwe jottems in het project; geen linked data, maar als distributie toegestaan |
| Webannotaties per project | GET /project/{projectId}/annotations (zie § 5.1 Ontsluiting: AnnotationCollection per jottem en per organisatie)
| application/ld+json (W3C anno-profile)
| ontsluit de verrijkingslaag als zelfstandig herbruikbare (deel)dataset; het aggregerende endpoint bestaat in de publieke API (containerindeling: per jottem) |
| IIIF Change Discovery | de ActivityStreams-feed per organisatie | application/ld+json
| maakt incrementeel harvesten vindbaar; kanttekening: de feed is organisatiebreed (activiteiten van alle projecten), harvesters filteren desgewenst op de Manifest-URI’s van het project |
Aanbeveling: alle kandidaten als extra distributies opnemen - de endpoints bestaan in de publieke API, het is louter een uitbreiding van de gegenereerde projectdatasetbeschrijving. Alle kandidaten zijn volledig afleidbaar uit de bestaande bronlagen; er is geen nieuwe brondata nodig.
6.4. E-depot-export: BagIt als verpakking, RO-Crate als beschrijving
Naast de RDF-datadump kan per project een compleet exportpakket worden aangemaakt waarmee al het materiaal van een project kan worden aangeboden aan het e-depot van een archiefinstelling (het SIP in OAIS-termen), en dat tevens de exit-strategie uit het keuzedocument concreet maakt:
-
**BagIt (RFC 8493) als omhulsel** - een zip met
data/-payload en checksummanifesten (manifest-sha512.txt,bag-info.txt), zodat de ontvangende instelling bit-integriteit bij ingest kan verifiëren. BagIt wordt native ondersteund door Archivematica en wordt probleemloos geaccepteerd door Preservica - de systemen achter de meeste Nederlandse e-depots. -
**RO-Crate als inhoudelijke beschrijving** - een
ro-crate-metadata.json(JSON-LD op basis van schema.org) die alle bestanden in het pakket beschrijft en relateert. Omdat de Jottem-metadata al schema.org AP NDE is, volgt de crate vrijwel direct uit de bestaande RDF-mapping; het pakket blijft daardoor ook búiten een e-depot zelfbeschrijvend en herbruikbaar.
{projectSlug}-{datum}.zip (BagIt)
├── bagit.txt / bag-info.txt project, organisatie, datum, aantallen
├── manifest-sha512.txt checksums van alle payload-bestanden
└── data/
├── ro-crate-metadata.json beschrijft alles (schema.org)
├── dump.nt.gz de RDF-datadump van het project
├── media/{mediaId}.(tif|jpg|…) originelen uit de Object Storage
├── metadata/{mediaId}.jsonld per jottem (schema.org AP NDE)
└── annotaties/{mediaId}.jsonld W3C Web Annotations per jottem
Een instellingsspecifieke metadatalaag (MDTO, of een E-ARK SIP/CSIP-structuur) wordt bewust niet standaard gegenereerd: Nederlandse e-depots verschillen daarvoor te sterk (zie de infrastructuuranalyse); zo’n laag is maatwerk per ontvangende instelling en kan bovenop de bag worden toegevoegd. METS/PREMIS genereert het e-depot zelf bij ingest.
Proces. De organisatiebeheerder start de export per project; een worker stelt het pakket asynchroon samen (originelen uit de Object Storage, metadata en annotaties uit de bronlagen) en plaatst het in een aparte exports-bucket met beperkte bewaartermijn. Zodra het pakket klaar is, ontvangt de organisatiebeheerder een e-mail met een directe downloadlink.
7. API-beschrijvingen
De API is gesplitst in een publieke lees-API (zonder authenticatie, voor bezoekers en API-gebruikers/harvesters) en een beheer-API (OIDC Bearer-authenticatie, voor uploaden, modereren en beheren).
7.1. Publieke API
7.2. Beheer-API
8. RSS
Twee feedtypen conform de RSS 2.0-specificatie: een platformbrede feed met nieuwe organisaties en per organisatie een feed met nieuwe jottems.
| Element | Bron |
|---|---|
channel title / link / description
| Organisatie.naam / organisatiepagina-URL / Organisatie.beschrijving
|
item title
| Media.titel
|
item link / guid
| duurzame jottem-URL |
item description
| Media.beschrijving + thumbnail
|
item pubDate
| Media.publicatieDatum
|
item enclosure
| Object Storage thumbs (met Media.mimeType)
|
9. Vocabulaires en termen
Vaste waardelijsten voorkomen vrije-tekstwildgroei en maken facetteren en linken mogelijk:
-
Genre/materiaaltype (
Media.genre): foto, menukaart, advertentie, folder, krantenartikel, vergunning, audio, overig - elk gekoppeld aan een term uit de Cultuurhistorische Thesaurus (CHT), opgezocht via het NDE Termennetwerk (zie hieronder). De waardelijst met bijbehorende CHT-URI’s is platformconfiguratie. -
Metadata-types (
Metadata.type): vervaardiger, datering, plaats, adres, openingsjaar, sluitingsjaar, persoon, gebouw, bedrijf, gebeurtenis, archiefbron, steekwoord, standpunt (WKT), zichtveld (WKT), herinnering. -
Annotatiemotivaties (
motivation, W3C-vocabulaire):describing(herinnering),commenting(reactie),identifying(persoon/gebouw/bedrijf op vlak),tagging(steekwoord),linking(archiefbron, gebeurtenis),editing(correctie). -
Term-URI’s: bij plaatsnamen, personen, gebeurtenissen en gebouwen wordt naast het label een URI vastgelegd (
Metadata.uriresp. annotation body), zodat de RDF-output linkbaar is.
9.1. NDE Termennetwerk (GraphQL)
Alle term-lookups (genres én term-URI’s) verlopen via de GraphQL API van het NDE Termennetwerk (https://termennetwerk-api.netwerkdigitaalerfgoed.nl/graphql), één externe API die zoeken in vele terminologiebronnen bundelt (CHT, Wikidata, GTAA, thesauri van RKD, RCE, e.a.):
-
de
sources-query levert de lijst van beschikbare terminologiebronnen; -
de
terms-query zoekt een term in één of meer gekozen bronnen en levert label + URI.
Terminologiebronnen per project. De organisatiebeheerder stelt per project in welke terminologiebronnen beschikbaar zijn voor term-URI’s (Project.terminologiebronnen, een lijst van bron-URI’s uit de sources-query; standaard staan alle bronnen open). De upload- en annotatie-interfaces beperken hun terms-queries tot die bronnen, zodat bijdragers per project passende thesauri aangeboden krijgen (bijv. CHT + Wikidata voor Smaak van Gouda). De backend biedt hiervoor een sources-proxy in de beheer-API.
10. Zoekindex en geo-data
Elasticsearch is een afgeleide store, gevuld door de indexeer-worker bij publicatie of wijziging (incl. annotatie-mutaties):
| Indexveld | Bron | Gebruik |
|---|---|---|
titel, beschrijving
| Media
| fulltext |
metadata_labels, steekwoorden
| Metadata
| fulltext + facet |
annotatie_teksten
| miiify (gesynct bij annotatie-mutatie) | fulltext (usecase: annotaties zoeken) |
genre, organisatie, project
| Media, Organisatie, Project
| facet |
periode
| Metadata datering/jaren
| facet/range |
locatie (geo_point/geo_shape)
| Metadata WKT/GeoJSON
| kaartzoeken |
publicatieDatum
| Media
| sorteren |
Geo-data wordt vastgelegd als WKT/GeoJSON in Metadata-rijen (adres/locatie van de jottem, standpunt fotograaf, zichtveld) en in annotatie-bodies, en gebruikt in: de kaart-frontend (MapLibre), de pand-tijdlijn (adres + openings-/sluitingsjaar), de koppeling met externe tijdmachines (zoals de Gouda Tijdmachine), Elasticsearch (geo-queries) en de RDF (schema:geo). Voor IIIF is de navPlace-extensie een optie om geo-informatie ook in manifests op te nemen.
11. Outputcontrole (traceability)
De veld→bron-tabellen in de voorgaande secties vormen de controle of alle benodigde data aan de outputkant beschikbaar is in de bronlagen. Daarbij zijn de volgende hiaten gevonden; deze zijn direct in het ERD verwerkt:
| Gevonden hiaat | Benodigd voor | Oplossing in datamodel |
|---|---|---|
| Geen titel/beschrijving als eigen velden | IIIF label/summary, RDF schema:name/description, RSS
| Media.titel, Media.beschrijving
|
| Geen licentie/rechten per jottem | IIIF rights, RDF schema:license, herbruikvoorwaarden
| Media.licentie (de uploader bevestigt bij het indienen de projectlicentie; die waarde wordt op de jottem vastgelegd)
|
| Geen afbeeldingsdimensies/formaat | IIIF info.json en canvas, RDF schema:width/height/encodingFormat
| Media.breedte, Media.hoogte, Media.mimeType (vastgelegd bij verwerking upload)
|
| Geen wijzigingsdatum | IIIF Change Discovery Update, RDF schema:dateModified
| Media.wijzigingsDatum + regel: elke relevante mutatie (ook annotaties) werkt deze bij
|
| Geen organisatiebeschrijving/website | publisher-informatie, IIIF provider, RSS-channel
| Organisatie.beschrijving, Organisatie.website
|
| Albumconcept onderontwikkeld (persoonlijk fotoboek vs. campagne) | projectpagina’s, datasetbeschrijving per project, IIIF Collection/RSS/annotaties per project | Project-entiteit op organisatieniveau (uuid, naam, slug, beschrijving, oproep, periode, afbeelding, datasetLicentie, status); Media.projectId verplicht - elke jottem hoort bij precies één project en elke organisatie heeft er minstens één
|
| Geen privacykeuze voor naamsvermelding | annotatie-creator, IIIF requiredStatement, RDF schema:contributor
| Gebruiker.naamPubliek
|
| Verwijderverzoeken wel in de API, niet in het datamodel | afhandelworkflow, depublicatie (status "gedepubliceerd", tombstone) | Verwijderverzoek-entiteit + status gedepubliceerd op Media
|
| Geen bron voor statistieken | statistieken per organisatie/project (logins, uploads, moderatie, annotaties) | Gebeurtenislog-entiteit; persoonsgebonden regels na bewaartermijn geaggregeerd/geanonimiseerd (AVG)
|
Aandachtspunten die geen datamodel-wijziging vragen maar wel een implementatieregel zijn:
-
Reproduceerbaarheid: Elasticsearch en Fuseki zijn volledig herbouwbaar uit PostgreSQL + miiify + Object Storage; een herindexeer-/hersync-taak hoort bij de beheervoorzieningen. Ook de e-depot-export is volledig afleidbaar uit deze bronlagen.
-
Depublicatie: bij een gehonoreerd verwijderverzoek worden alle outputs geschoond (index, RDF/named graph, feeds, IIIF-cache-purge) en resteert een tombstone op de duurzame URL.
-
Audio (fase 2):
durationwordt bij verwerking van audio-uploads vastgelegd (technische metadata bij het mediabestand) ten behoeve van het IIIF-canvas. -
Vocabulaire-URI’s (genre e.d.) zijn configuratie, geen databasemodel; ze moeten wel versioneerbaar beheerd worden. De per project beschikbare terminologiebronnen komen uit
Project.terminologiebronnen(zie § 9.1 NDE Termennetwerk (GraphQL)).