From c459fa0a60c9956d77b9e8802bbc3905490f93f3 Mon Sep 17 00:00:00 2001 From: Edwin van den Houdt Date: Tue, 21 Jul 2026 07:46:38 +0200 Subject: [PATCH] feat(stamdata): extract policy-question text into Stamdata Move the geldigheidsvragen wording out of DiplomaRules into Stamdata.PolicyQuestions (business-editable text, config-as-code); DiplomaRules keeps only the rule of which questions apply. Extend StamdataValidationTests (no blank id/wording, distinct ids in the manual set) and update ADR-0004. Co-Authored-By: Claude Opus 4.8 --- .../Domain/Diplomas/DiplomaRules.cs | 32 ++----------- .../Stamdata/PolicyQuestions.cs | 45 +++++++++++++++++++ .../StamdataValidationTests.cs | 17 +++++++ .../architecture/0004-stamdata-as-code.md | 9 ++-- 4 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 backend/src/BigRegister.Api/Stamdata/PolicyQuestions.cs diff --git a/backend/src/BigRegister.Api/Domain/Diplomas/DiplomaRules.cs b/backend/src/BigRegister.Api/Domain/Diplomas/DiplomaRules.cs index cc2a2bc..effe9b8 100644 --- a/backend/src/BigRegister.Api/Domain/Diplomas/DiplomaRules.cs +++ b/backend/src/BigRegister.Api/Domain/Diplomas/DiplomaRules.cs @@ -19,44 +19,20 @@ public static class DiplomaRules /// Professions a user may declare for a manual (unlisted) diploma. public static IReadOnlyList ManualProfessions() => Professions.All(); - // --- Policy questions (geldigheidsvragen) --- - - private static readonly PolicyQuestion NlTaalEngelstalig = new( - "nl-taalvaardigheid", - "Uw opleiding was Engelstalig. Beheerst u de Nederlandse taal op het vereiste niveau (B2)?", - QuestionType.JaNee); - - private static readonly PolicyQuestion NlTaalManual = new( - "nl-taalvaardigheid", - "Beheerst u de Nederlandse taal op het vereiste niveau (B2)?", - QuestionType.JaNee); - - private static readonly PolicyQuestion DiplomaErkend = new( - "diploma-erkend", - "Is uw diploma erkend door de Nederlandse overheid (bijv. via Nuffic)?", - QuestionType.JaNee); - - private static readonly PolicyQuestion Toelichting = new( - "toelichting", - "Geef een korte toelichting op uw diploma en opleiding.", - QuestionType.Tekst); - /// - /// RULE: an English-language diploma requires proof of Dutch proficiency (B2). - /// Add a question here to apply it to a (set of) diploma(s) — a single backend - /// change, no frontend change. + /// RULE: an English-language diploma requires proof of Dutch proficiency (B2). Which + /// question applies is the rule; its wording is stamdata in . /// public static IReadOnlyList QuestionsFor(Diploma d) { var questions = new List(); if (d.Engelstalig) - questions.Add(NlTaalEngelstalig); + questions.Add(PolicyQuestions.NlTaalEngelstalig); return questions; } /// /// RULE: a manual diploma is unverified, so the strictest (maximal) set applies. /// - public static IReadOnlyList ManualQuestions() => - new[] { NlTaalManual, DiplomaErkend, Toelichting }; + public static IReadOnlyList ManualQuestions() => PolicyQuestions.ManualSet; } diff --git a/backend/src/BigRegister.Api/Stamdata/PolicyQuestions.cs b/backend/src/BigRegister.Api/Stamdata/PolicyQuestions.cs new file mode 100644 index 0000000..f496fd9 --- /dev/null +++ b/backend/src/BigRegister.Api/Stamdata/PolicyQuestions.cs @@ -0,0 +1,45 @@ +using BigRegister.Domain.Diplomas; + +namespace BigRegister.Stamdata; + +/// +/// BUSINESS-EDITABLE STAMDATA (config-as-code). The geldigheidsvragen (policy questions) +/// shown for a diploma, and their exact wording. This is the text the business tunes. +/// +/// Change it by editing this file and opening a PR — NOT via a production database. The +/// compiler catches shape/type mistakes; StamdataValidationTests catches the rest +/// (blank ids/wording, duplicate ids). See ADR-0004. +/// +/// This is DATA, not logic: WHICH questions apply to which diploma (English → B2, manual → +/// maximal set) is a rule and stays in DiplomaRules. +/// +public static class PolicyQuestions +{ + public static readonly PolicyQuestion NlTaalEngelstalig = new( + "nl-taalvaardigheid", + "Uw opleiding was Engelstalig. Beheerst u de Nederlandse taal op het vereiste niveau (B2)?", + QuestionType.JaNee); + + public static readonly PolicyQuestion NlTaalManual = new( + "nl-taalvaardigheid", + "Beheerst u de Nederlandse taal op het vereiste niveau (B2)?", + QuestionType.JaNee); + + public static readonly PolicyQuestion DiplomaErkend = new( + "diploma-erkend", + "Is uw diploma erkend door de Nederlandse overheid (bijv. via Nuffic)?", + QuestionType.JaNee); + + public static readonly PolicyQuestion Toelichting = new( + "toelichting", + "Geef een korte toelichting op uw diploma en opleiding.", + QuestionType.Tekst); + + /// The maximal set applied to an unverified (manual) diploma. + public static readonly IReadOnlyList ManualSet = + new[] { NlTaalManual, DiplomaErkend, Toelichting }; + + /// Every question defined here — used by the stamdata validation gate. + public static readonly IReadOnlyList All = + new[] { NlTaalEngelstalig, NlTaalManual, DiplomaErkend, Toelichting }; +} diff --git a/backend/tests/BigRegister.Tests/StamdataValidationTests.cs b/backend/tests/BigRegister.Tests/StamdataValidationTests.cs index aa927f7..6b440f5 100644 --- a/backend/tests/BigRegister.Tests/StamdataValidationTests.cs +++ b/backend/tests/BigRegister.Tests/StamdataValidationTests.cs @@ -38,4 +38,21 @@ public class StamdataValidationTests Assert.NotEmpty(professions); Assert.Equal(professions.Count, professions.Distinct().Count()); } + + [Fact] + public void Every_policy_question_has_an_id_and_wording() + { + Assert.All(PolicyQuestions.All, q => + { + Assert.False(string.IsNullOrWhiteSpace(q.Id), "A policy question has a blank id."); + Assert.False(string.IsNullOrWhiteSpace(q.Vraag), $"Policy question '{q.Id}' has blank wording."); + }); + } + + [Fact] + public void Manual_question_set_has_distinct_ids() // no field is asked twice + { + var ids = PolicyQuestions.ManualSet.Select(q => q.Id).ToList(); + Assert.Equal(ids.Count, ids.Distinct().Count()); + } } diff --git a/docs/reference/architecture/0004-stamdata-as-code.md b/docs/reference/architecture/0004-stamdata-as-code.md index a69f5ab..7dde07c 100644 --- a/docs/reference/architecture/0004-stamdata-as-code.md +++ b/docs/reference/architecture/0004-stamdata-as-code.md @@ -75,7 +75,8 @@ everyone. Stamdata (the rules and reference tables the whole register runs on) s - **−** A change needs the PR pipeline — not instant, and a non-developer may need dev assistance to edit C# (mitigated later by a low-code editor that emits a PR, or by a data-file format if hand-editing ergonomics ever outweigh maximal compile-time safety). -- **Pilot shipped with this ADR:** the profession↔diploma map extracted to - `Stamdata/Professions.cs`, `DiplomaRules` refactored to consume it (behaviour unchanged), - and `StamdataValidationTests` added. Policy-question text and document-category - definitions follow the same pattern as obvious next steps; not moved yet. +- **Shipped with this ADR:** the profession↔diploma map (`Stamdata/Professions.cs`) and the + policy-question wording (`Stamdata/PolicyQuestions.cs`) extracted from `DiplomaRules`, + which now consumes both (behaviour unchanged), guarded by `StamdataValidationTests`. + Document-category definitions follow the same pattern as the obvious next step; not moved + yet.