Dutch Repository Federation

Data governance for a federated repository ecosystemDraft

Using data products and data contracts for Dutch repositories in a data mesh architecture

In the Dutch Repository Federation, every party delivers its data as a product, according to a contract. That's how Dutch research output becomes resilient, open and global.

A while ago I wrote a post for the BROCCOLI team arguing that they should borrow a few ideas from data mesh architecture. BROCCOLI is building a data lake: it harvests OpenAlex, OpenAIRE, Crossref and the Dutch CRIS systems into one place and refines them through bronze, silver and gold layers into dashboards. Data products, data contracts and a marketplace fit that picture well.

At first sight, the Dutch Repository Federation (DURF) looks like a different animal. The publications themselves, and the authoritative records about them, stay where they are: at the universities. The information chain in the position paper runs through systems that already exist and are run by different parties:

The DURF proposal puts it plainly: the project "does not build a single technical infrastructure with a single point of failure. Rather it leverages existing components and standards … and creates the necessary links between them."

So no lake, then? That's what I thought when I started this post. But follow the metadata, not the files, and a lake shows up after all:

That's a data lake with medallion layers. It just isn't ours: OpenAIRE runs it, and the federation pays for it through its licence contributions. It turns out to be the key to how the whole chain hangs together, so I've given it its own section below.

Two things carry over from the BROCCOLI post unchanged, and in a federation they matter even more: data as a product, and data contracts first.

The idea in one paragraph

Every link in the DURF chain relies on the link before it. The KB can only preserve a file if the repository says where the file is. The Portal can only show a university's output if the records carry that university's ROR ID. Google Scholar can only index an article if the landing page has the right meta tags. Today those dependencies are mostly implicit, and they hold as long as someone happens to remember them. My proposal is to make each one explicit. At every point where data passes from one party to another, the delivering party treats what it hands over as a product, owned by a named data team. It then writes down what it promises in a data contract: which fields, in which format, how complete, how fresh, and how you can check. The receiving party builds against the contract, not against whatever it happened to find last week.

What we keep, what we leave out

Keep: data products with an owner and a team; data contracts in the Open Data Contract Standard; federated governance; contracts first, starting from what the consumer needs; and the medallion layers, with the OpenAIRE Graph as the lake.

Leave out: a lake of our own; one shared pipeline stack. Each party keeps its own systems and its own tooling. What we share are the contracts, and one lake that someone else runs for us.

What it looks like

Here's the DURF information chain again, redrawn as data products. Each dashed frame is one data product, with the party that owns it. Each purple box is a contract: an output port that the producer promises to keep. The contract lives with the producer, because the producer is the one making the promise. Purple arrows are the dependencies the contract covers.

The DURF information chain as data products and data contracts Four data products, each with an owner. Publishing platforms (publisher_deliveries, outside DURF governance) deliver full text and CC0 metadata to institutional repositories under contract C3. Each university library (cris_publications) offers two output ports: C1, CRIS publications in the Dutch application profile, harvested by OpenAIRE PROVIDE, and C2, landing pages and sitemaps for other indexes. OpenAIRE (openaire_graph: PROVIDE, Graph, Broker) works as the shared data lake: harvested metadata as bronze, the deduplicated Graph as silver, and three gold output ports: C4, enrichment events back to the institutions; C5, the Dutch slice of the Graph for the Netherlands Research Portal and other indexes; and C6, a preservation feed for the KB e-Depot. The KB (edepot_preservation) offers C7, preservation status and URN:NBN, back to the institutions and to the URN:NBN resolver. publisher_deliveries Owner: each publisher Outside DURF governance Publishersand preprint servers C3 · Full textJATS + files, CC0 cris_publications One per university library Owner: the library's data team Repository / CRISpublications and full texts C1 · CRIS publicationsDutch application profile C2 · Discoverylanding pages, sitemap Consumes C3, C4 and C7 Data owner · steward · engineer openaire_graph Owner: OpenAIRE The lake: bronze → silver → gold PROVIDEharvested and validated OpenAIRE Graphdeduplicated, united Broker C4 · Enrichment events C6 · e-Depot feed C5 · Dutch Graph slice BRONZE SILVER GOLD edepot_preservation Owner: KB KB e-Depotlong-term preservation C7 · Preservationstatus, URN:NBN, fixity URN:NBN resolver NL Research PortalOpenAIRE CONNECT Other indexesOpenAlex, Scholar, EOSC Data product, with its owner Contract: output port, kept by the producer Data System or consumer Covered by a contract
The DURF information chain as four data products and seven contracts. The OpenAIRE Graph is the one place where the metadata comes together. It works as a lake with bronze, silver and gold layers, run by OpenAIRE. Each party keeps its own data and full texts in its own systems, and the purple boxes are the only interfaces between them. Handoffs inside a single party, such as PROVIDE → Graph → Broker inside OpenAIRE, are that party's own business (grey arrows).

The seven example contracts are in the contracts/ folder of this repository. They're all drafts. Each one is owned by one work package of the DURF project, which gets it agreed and adopted. More on that below.

ContractProducer → consumerWork package
C1durf-cris-publications-v1University library → OpenAIRE PROVIDE2 Metadata quality
C2durf-repository-discovery-v1University library → search engines and indexes5 Discovery
C3durf-publisher-fulltext-v1Publishing platform → university library3 Full text
C4durf-broker-enrichment-events-v1OpenAIRE Broker → university library2 Metadata quality
C5durf-openaire-graph-nl-publications-v1OpenAIRE Graph → Research Portal, other indexes6 Portal
C6durf-openaire-graph-edepot-feed-v1OpenAIRE Graph → KB e-Depot4 e-Depot
C7durf-edepot-preservation-status-v1KB e-Depot → university library, URN:NBN resolver4 e-Depot

The OpenAIRE Graph is our data lake

In the BROCCOLI post, the medallion layers were the backbone of the design. They're here too; they just sit inside one party. Here's the same table for DURF:

LayerWhat it holdsIn DURFContract
SourceThe authoritative records and the full textsEach repository and CRIS, run by the university libraryC1 describes what goes out; C3 what comes in from publishers
BronzeMetadata as harvested and validated, one set per sourceOpenAIRE PROVIDE harvests every Dutch source, next to Crossref, ORCID, ROR, DataCite and the restC1 fixes what the Dutch bronze contains
SilverOne deduplicated, cross-linked record per research productThe OpenAIRE Graph itself: Dutch output united with the rest of the world'sNone from DURF: this is OpenAIRE's internal business
GoldPurpose-built outputs, one per useThe Dutch slice for the Portal and the indexes (the Portal's netherlands community in the Graph, through the community filter in the Graph API or the community data dump); the preservation feed for the e-Depot and, through it, the resolver; enrichment events back to the repositoriesC5, C6 and C4

C2 (landing pages for search engines) and C3 (deliveries from publishers) sit outside the lake. They go straight from or to the repositories.

Seeing the Graph as a lake makes three things clear.

DURF doesn't run the lake, so it steers at the edges. BROCCOLI builds and owns its whole lake, from bronze to gold. DURF only owns what goes in and what comes out. The bronze contract (C1) and the gold contracts (C4, C5, C6) are DURF's grip on the lake. Silver, the matching and deduplication, is OpenAIRE's craft and OpenAIRE's responsibility. DURF's lever there is the licence, negotiated through NaMeCo, not a say in the pipeline.

This time we produce our own bronze. BROCCOLI has to take OpenAlex as it comes, so its bronze contracts can only describe what arrives. In DURF, the Dutch bronze comes from the federation's own members. That makes C1 a real promise, not a description: when the Portal needs a ROR ID in gold, the fix is at the source that feeds bronze, and that source is one of us.

A lake is not a single point of failure here. The proposal says DURF shouldn't build a single point of failure, and it doesn't. The Graph holds a copy of the metadata, not the only copy. The authoritative records and the full texts stay at the universities. The bronze can be harvested again at any time. The Graph is published as open dumps on Zenodo. And the e-Depot keeps its own preserved copy. If the lake ever went away, it could be rebuilt from the sources. That's what federating around a lake, rather than into one, means.

There's a nice bonus. SURF's BROCCOLI lake reads the same data for two of its bronze sources. Its openaire_publications is the Dutch slice of the Graph, from the community data dump or the community filter in the Graph API: exactly C5. Its cris_<institution> harvests the same CRIS endpoints that C1 covers. So the gold that DURF gets from OpenAIRE is bronze for BROCCOLI: lakes feeding lakes, held together by contracts. BROCCOLI doesn't need its own bronze contracts for these two sources. It can reuse C1 and C5, and when a university improves its metadata to meet C1, the Portal and BROCCOLI's quality dashboard both see it.

Every university gets a data team

In data mesh, a data product isn't just a dataset. It's the data plus the code that produces it, the contract that describes it, and above all a team that's accountable for it. For DURF that means every participating institution runs its repository or CRIS as a data product called cris_publications, owned by a small data team with three roles:

That sounds like a lot of new people, but it isn't. These are the people who already run the repository. The DURF proposal counts on "approximately 0.1–0.2 FTE per institution allocated from existing repository and CRIS managers' responsibilities" after the project ends. What changes isn't the headcount. It's that their work now has a clear shape: here is our product, here is what we promise, here is how anyone can check.

The same goes for the other parties. OpenAIRE runs openaire_graph as one data product with three output ports: enrichment events for the institutions, the Dutch slice of the Graph for the Portal and the indexes, and a preservation feed for the KB. The KB runs edepot_preservation and reports back what it has safely stored. Each has its own data team, its own tooling and its own release rhythm. The only thing the others see is the contract.

This is also what the position paper means when it says parties, not systems, take on commitments. A CRIS can't sign a pledge. The data team behind it can, and a data contract is how that pledge becomes specific enough to check. Each contract makes one cell of the roles and responsibilities matrix concrete: the matrix says who can expect something from whom, the contract says exactly what.

The first contract: a Dutch application profile

Everything downstream depends on C1, the contract between each institution and OpenAIRE. If a record reaches the Graph without a DOI, the Graph can't deduplicate it. If it arrives without a ROR ID, the Portal can't attribute it to the right university. Without a file location, the KB has nothing to preserve. Without an access right, nobody downstream knows what they may show.

The good news is we don't have to invent C1. The OpenAIRE Guidelines for CRIS Managers 1.2 already define the structure and vocabularies:

What the guidelines can't know is what Dutch consumers downstream rely on. So C1 takes the guidelines as its base and tightens a handful of fields. That tightened contract is the Dutch application profile the DURF proposal wants to develop, replacing the current NL-DIDL/MODS requirements.

FieldOpenAIRE CRIS 1.2Dutch profileWho relies on it
DOIoptionalmandatory if it existsGraph deduplication, every index
Author ORCIDrecommendedmandatory if knownGraph, Portal, researcher profiles
Affiliation ROR IDoptionalmandatory for own authorsPortal attribution, monitoring
Access rights (COAR)mandatory if applicablemandatorye-Depot, Portal, indexes
Embargo end datenot a separate fieldmandatory if embargoedTaverne, e-Depot access policy
Licenceoptionalmandatory if applicablePortal, reuse
File location + MIME typeoptionalmandatory for every file, open or note-Depot, full-text monitoring
File checksumn/arecommendede-Depot fixity check

Written down as a data contract, the relevant part looks like this. Most of the file is descriptions, because the descriptions are the point: a CRIS manager at any university should be able to read it and know what to do.

# contracts/institutions/durf-cris-publications-v1.odcs.yaml (shortened)
apiVersion: v3.1.0
kind: DataContract
id: durf-cris-publications-v1
name: Institutional publications, Dutch application profile of the
      OpenAIRE Guidelines for CRIS Managers 1.2
version: 0.1.0
status: draft
domain: institutional-research-output
dataProduct: cris_publications
customProperties:
  - property: durfTheme
    value: 2
  - property: oaiPmhMetadataPrefix
    value: oai_cerif_openaire
  - property: replaces
    value: NL-DIDL/MODS (EduStandaard)
team:
  name: institution-data-team
  members:
    - { username: data-owner,    role: Data owner }
    - { username: data-steward,  role: Data steward }
    - { username: data-engineer, role: Data engineer }
slaProperties:
  - { property: latency, value: 7, unit: d }        # in CRIS → on the endpoint
  - { property: retention, value: persistent }      # deletions are announced, never dropped
servers:
  - server: production
    type: api
    location: https://cris.institution.example/ws/oai?verb=ListRecords&...
schema:
  - name: publication
    physicalName: cerif:Publication
    properties:
      - name: doi
        physicalName: DOI
        description: >-
          Mandatory whenever the publication has one (MA). The OpenAIRE
          guidelines leave it optional; NL requires it, because it is the
          main deduplication key in the Graph and for the indexes.
      - name: authors
        items:
          properties:
            - name: orcid                   # MA in NL (R in OpenAIRE)
            - name: affiliation_ror         # MA in NL: at least the own institution
      - name: access_rights
        required: true                      # M in NL: e-Depot, Portal and indexes branch on it
      - name: embargo_end_date              # NL addition, for Taverne
      - name: file_locations                # MA for every file, whatever its access rights
    quality:
      - type: custom
        engine: openaire-provide-validator
        description: >-
          The OpenAIRE PROVIDE validator for the CRIS Guidelines 1.2 profile
          passes with no errors. This is the contract test.
      - type: text
        description: Every author affiliated with the producing institution
          carries the institution's ROR ID.

Two things I like about this. First, the authoritativeDefinitions in the full file point to the OpenAIRE guidelines. So the Dutch profile is visibly a thin layer on top of an international standard, not a national fork. Second, the quality section says out loud what the contract test is: the OpenAIRE PROVIDE validator, which already runs on every harvest. The Dutch additions, like "every own author has the ROR ID", are simple enough to check next to it.

Who owns the contract itself, as opposed to the data? In a federation, not the individual institution: then there would be seventy slightly different profiles. The DURF proposal has the answer ready. A NL Research Information Content Board, formed through EduStandaard, stewards the profile. Institutions implement it. The board changes it through a public process. That's federated governance in its purest form: we agree on a few rules together, and each data team makes sure they're met.

OpenAIRE and the KB are data teams too

It would be easy to read all this as "the universities must deliver, and everyone else consumes". But the chain only works if the promises run both ways. In the diagram, four of the seven contracts are kept by OpenAIRE or the KB, not by the universities:

A word on the "Broker to Graph" and "Graph to e-Depot" steps. PROVIDE, the Graph and the Broker are all run by OpenAIRE, so I've drawn them as one data product with three output ports, rather than three products with contracts between them. Contracts matter where responsibility changes hands. Inside OpenAIRE, OpenAIRE may well use contracts between its own teams, and that would be good practice. But DURF doesn't need to see them. What DURF needs is a firm promise at each point where data leaves OpenAIRE: towards the institutions (C4), the Portal and the indexes (C5), and the KB (C6). In lake terms, bronze, silver and gold all live inside this one product, and DURF's contracts sit at its edges: C1 going in, C4, C5 and C6 coming out.

Contracts with parties outside DURF

Two of the contracts cross the federation's border, and they behave differently.

C3, publisher deliveries, runs from publishers into the repositories: JATS metadata (CC0) plus the full-text files, delivered over SWORD, routed on the institution's ROR ID. Publishers don't sign the DURF position paper. So this contract only has force where it's written into an agreement, such as the open access terms of a national read-and-publish deal. Without one, it still has a use: it describes exactly what a DURF repository is ready to receive. Handing a publisher a contract that fits on one page makes that conversation a lot easier.

C2, discovery, runs the other way, from the repositories to Google Scholar, OpenAlex, BASE, CORE and anyone else with a crawler. Those consumers will never sign anything either. Here the contract is a one-sided promise: every publication gets a stable landing page with citation_* meta tags, a working citation_pdf_url, and schema.org JSON-LD, all listed in a sitemap. DURF checks that promise on behalf of the consumers.

The monitors in the proposal are contract tests

This is where it clicked for me. The DURF proposal's software management plan lists five monitors to build. Put them next to the contracts:

Monitor in the DURF proposalChecks the contract
Metadata compliancy: validates repositories and CRIS systems against the OpenAIRE CERIF standard and the Dutch application profileC1, schema and quality rules
Core PID comprehensiveness: missing DOIs, ORCIDs, RORs, grant IDsC1, the NL tightenings
Full-text comprehensiveness: records without full text, broken linksC1 file_locations; C3 deliveries
Preservation status: full text stored in the e-DepotC6 and C7
Visibility: robots.txt, sitemap.xml, schema.org metadataC2

Every monitor is a contract test, and every contract gets a monitor. That gives DURF a nice design rule: no monitor without a contract, and no contract without a monitor. The contract says what "good" means. The monitor shows, per institution, how far off we are. And because the contract is written by the whole federation, a red cell on a dashboard isn't one party pointing at another. It's everyone looking at the same agreement.

We might not even have to build the dashboard from scratch. The ORI Quality Monitoring dashboard from PID2Portal already shows data stewards how complete their institution's metadata is, per source. A DURF tab that reads the contract tests would sit very naturally next to it.

What this means for each work package

The DURF proposal splits the project into six work packages, the themes, each with its own activities and months. So how does all this land in the project plan? The short version:

The months are those in the proposal.

Theme 1 · Governance of the Federation and the Portal

Owns: the agreements that the contracts belong to.

Activity in the proposalMonthsWhat the data mesh approach adds
Develop and secure signatures for the position paper1–12The pledges say what each party commits to. The contracts say exactly what that means, and each links back to its pledge.
Formal agreements on roles and responsibilities between SURF, NaMeCo institutions, the KB, publishers and OpenAIRE1–18Each agreement gets the contracts that party keeps as a technical annex. The agreement is the promise; the annex is the part you can test.
Legal structure for CC0 metadata influx6Becomes the licence terms in C3 (metadata from publishers) and C1 (metadata exposed by the repositories).
Governance framework for all project themes1–12Includes how a contract changes: versions, pull requests, who approves (see below).
Establish NaMeCo1–12The natural counterpart for OpenAIRE's contracts C4, C5 and C6: it speaks for the Dutch consumers.
Review and update the governance framework for sustainability36–48Hands each contract to a permanent owner. C1 goes to the Content Board, C4–C6 to NaMeCo, and keeping them goes to the institutions' data teams, within the 0.1–0.2 FTE.

Theme 2 · Metadata Quality Improvement

Owns: C1 (the Dutch application profile) and C4 (enrichment events).

Activity in the proposalMonthsWhat the data mesh approach adds
Form the NL Research Information Content Board through EduStandaard1–3The Content Board becomes the owner of C1.
Develop the updated metadata application profile3–12C1 is the draft profile. Deliverable: C1 version 1.0.
Support repositories, CRIS systems and the e-Depot in adopting the profile12–18Each institution compares its OAI-PMH output with C1. The gap list is its work plan.
Monitor compliance and share best practices; monitor core PIDs12–48These monitors are the tests of C1: the PROVIDE validator plus the Dutch rules.
Proof of concept for enriching local metadata with the OpenAIRE Broker12–24C4. The proof of concept shows whether the events are useful enough to commit to.
Download statistics sharing pilot on OpenAIRE12–18A new contract, not drafted yet: usage statistics from the repositories to OpenAIRE.

Theme 3 · Full Text Capture Enhancement

Owns: C3 (publisher deliveries), together with Theme 1 for the agreements with publishers.

Activity in the proposalMonthsWhat the data mesh approach adds
Baseline metrics and reporting for full-text uptake1–6Measure today's completeness of file_locations in C1, per institution. That is the baseline.
Inventory of tools and methods for full-text capture1–12Includes delivery by publishers. C3 describes what a repository can receive.
Develop and pilot a local full-text capture service12–24The service is the consumer of C3, and delivers the files that C1 exposes.
Ecosystem-wide improvements to full-text availability24–48Write C3 into the national agreements with publishers, with Theme 1.

Theme 4 · KB e-Depot Archiving

Owns: C6 (the e-Depot feed) and C7 (preservation status).

Activity in the proposalMonthsWhat the data mesh approach adds
Pull mechanism for metadata and full text, partly via OpenAIRE3–12C6 is the "via OpenAIRE" part. The "partly" may need an extra contract between the KB and each institution for restricted files.
Archive and back up all Dutch CRIS and repository content12–48C6 in production, for every institution that meets C1.
Monitoring system for preservation status12–48The monitor reads C7.
Update and maintain the URN:NBN resolver12–18The resolver consumes C7, and the URN:NBN flows back into C1.

Theme 5 · Distribution & Discovery Optimization

Owns: C2 (landing pages and sitemaps).

Activity in the proposalMonthsWhat the data mesh approach adds
Mappings and best practices for major indexes1–12These become the content of C2, and of the index side of C5.
DOI minting for grey literature6–18More records can fill doi in C1, the key deduplication field downstream.
Monitoring system for DURF content visibility in major indexes12–48The test of C2.

Theme 6 · Portal Development and Enhancement

Owns: C5 (the Dutch slice of the Graph).

Activity in the proposalMonthsWhat the data mesh approach adds
Netherlands Research Portal Steering Committee1, 25Speaks for the consumers of C5.
Regular stakeholder needs assessments6, 18, 30, 42This is "contracts first" in practice. Needs lead to changes in C5, and where necessary in C1.
Development roadmaps and portal releases9–48Each roadmap lists the contract changes it depends on. A major portal release can bring a new version of C5.
SSO to OpenAIRE services with SURFconext9No contract needed: this is about access, not data.

Each contract file records the same thing in two properties, workPackage and workPackageActivities, so you can see from the YAML which part of the project it belongs to.

Contracts first, starting from the consumer

In the BROCCOLI post, my main plea was to start from the user journey and not from the data. The same holds here, just with the chain instead of a dashboard. Don't start by asking "what can our CRIS export?". Start at the far end of the chain and work back:

  1. What do the end consumers need? The e-Depot needs files with checksums. The Portal needs ROR IDs and access rights. The indexes need DOIs and PDF links. That fixes the gold contracts C5 and C6, plus C2.
  2. What must OpenAIRE receive to deliver that? That fixes C1, the contract for what goes into bronze. It's why C1 is stricter than the plain OpenAIRE guidelines. In between, ask OpenAIRE whether its silver can connect the two ends.
  3. What do the institutions need back to keep their promise? That fixes C4 and C7, and what they need from publishers fixes C3.
  4. Only then: the mapping in each CRIS, the OAI-PMH configuration, the harvesting and feeds. Each data team builds its own part with its own tools, and tests it against the contract.

A house rule to go with it: no field in the Dutch profile without a consumer downstream who needs it. Every NL tightening in C1 names who relies on it. If nobody does, it goes. That keeps the profile small, and it gives the Content Board a simple test for every change request.

How contracts change

Contracts are YAML files in a git repository, so they change the way code does: through a pull request, reviewed by the parties who depend on them. I'd suggest a few simple rules.

The move from NL-DIDL/MODS to the CERIF profile is, in these terms, exactly such a major version change. We'll learn how to run a transition period by doing that one well.

Things I'm not sure about yet

So, where to start? A timeline

Put together, this is how the contracts could move through the project. The months and themes are the proposal's own.

MonthsWhat happensTheme
1–3The Content Board is formed and takes C1 as its starting draft. The Portal Steering Committee is formed to speak for the consumers of C5.2, 6
1–6Two or three pilot institutions, on different platforms, compare their OAI-PMH output with C1. The full-text baseline is measured on the same output.2, 3
3–12C1 version 1.0 is agreed as the Dutch application profile. The C6 feed runs between OpenAIRE and the KB. The index mappings are written into C2.2, 4, 5
6The CC0 legal structure is settled and written into C1 and C3. The first Portal needs assessment feeds into C5.1, 6
12The position paper is signed. The governance framework includes how contracts change.1
12–18Institutions adopt C1, and the first monitors run as contract tests. URN:NBNs flow back through C7. At month 18 the formal agreements are signed, with the contracts as annexes.1, 2, 4
12–24The Broker proof of concept tests C4. The capture service pilot tests C3.2, 3
24–48All contracts are in production, with their monitors on one dashboard. C3 goes into the publisher agreements.all
36–48Each contract is handed to a permanent owner for after the project.1

Meanwhile, the contracts live in this repository. datacontract lint should run on every pull request, and each contract should be linked from the pledge in the position paper that it makes concrete.

None of this needs a lake of our own, a new platform or a big central team. It needs each party to say out loud what it delivers, write that down in a format the others can read and test, and keep the promise. That's what makes Dutch research output resilient, open and global: not one strong system, but a chain of kept promises.

The contracts are drafts and so is this post. If you run a repository, a CRIS, the Graph or the e-Depot and you think I've got your part wrong, please open an issue or send a pull request. That's how contracts are supposed to change anyway.