## What & why S-13: a diploma's origin decides its route. A **DMN** (`diploma-eligibility`) is evaluated inline by the registratie process as a **`businessRuleTask`**; an exclusive gateway routes a **foreign** (Buitenlands) diploma through a new **CBGVAdvies** user task before `Beoordelen`, a **domestic** one straight there (PRD flow 4). The domain's only new job is carrying the diploma origin and passing it as a process start variable. Chose **Option B (DMN in the BPMN)** over the issue's literal "evaluated by the Domain Service via Workflow Client" wording — keeps the decision a first-class workflow artefact and §8.2 clean. Rationale in **ADR-0016** (proposal #100); noted on this issue. Closes #14 ## Definition of Done - [x] Linked Gitea issue (above). - [x] Failing test committed before the implementation. - [x] Implementation makes the test pass. - [x] Conventional Commits referencing the issue (`refs #14`). - [ ] CI green — all Gitea Actions jobs. - [x] `docker compose up` from a fresh clone reaches green health checks within 3 minutes (additive; DMN deployed by flowable-init). - [x] Docs updated (ADR-0016, demo note). - [x] ADR added (`docs/architecture/adr-0016-diploma-eligibility-dmn.md`). - [x] Demo note in `docs/demo-script.md`. ## How it was built (TDD) - **Domain**: `DiplomaOrigin` on the aggregate + submit command; threaded through the process-start port so the Workflow Client emits a `diplomaOrigin` start variable. Red → green. - **DMN + BPMN**: `workflows/diploma-eligibility.dmn` (origin → route); `businessRuleTask` + exclusive gateway + `CBGVAdvies` user task in `registratie.bpmn`; DMN deployed to Flowable's DMN engine by `flowable-init`. - **Both paths**: `Een diploma op herkomst routeren` acceptance scenarios (origin carried into the process) + unit tests; verify-domain drives a foreign registration through CBGVAdvies→Beoordelen and the domestic one straight to Beoordelen — exercising both DMN branches live. ## Notes for reviewers - Deviation from the issue's Option-A wording is deliberate and recorded (ADR-0016); the outcome is unchanged. - The self-service eIDAS→foreign wiring is out of scope here (this slice is area:domain + area:workflow); the domain submit accepts an optional `diplomaOrigin` so the foreign path is drivable. - Local green: domain unit 109, acceptance 15, `dotnet format`, Release build (0 errors), **domain mutation 95.39%** (break 90). The DMN/`businessRuleTask` REST wiring is CI-verified on verify-stack (no local full-stack run here). Reviewed-on: #101
78 lines
5.2 KiB
Markdown
78 lines
5.2 KiB
Markdown
# ADR-0016: Diploma eligibility is a DMN evaluated inline as a BPMN DMN service task
|
|
|
|
- **Status:** Accepted
|
|
- **Date:** 2026-07-17
|
|
- **Deciders:** Respellion engineering
|
|
- **Relates to:** S-13 (#14); proposal #100. Builds on ADR-0009 (external-task worker / Workflow
|
|
Client), ADR-0014/0015 (the boundary-event and routing constructs on the registratie process).
|
|
|
|
## Context
|
|
|
|
S-13 adds flow 4: a foreign diploma must get an extra CBGV-advies assessment before beoordeling
|
|
(PRD §5). The eligibility decision — domestic goes straight to beoordeling, foreign routes through
|
|
CBGV-advies — needs a home. The Flowable REST app bundles a DMN engine, and the same
|
|
`repository/deployments` machinery that deploys `registratie.bpmn` can deploy a `.dmn`. §8.2 makes
|
|
the Workflow Client the only code that talks to Flowable; the PRD frames the workflow as "BPMN + DMN
|
|
governing the registration workflow" (Flowable as a peer orchestration module).
|
|
|
|
The issue's wording ("a DMN decision table evaluated by the Domain Service via Workflow Client")
|
|
suggests the domain reaches into Flowable's DMN API to evaluate the decision and feeds the result
|
|
back. That is one option; it is not the only one, and it is not the cleanest.
|
|
|
|
## Decision
|
|
|
|
**The diploma-eligibility DMN is deployed to Flowable and evaluated inline by the registratie process
|
|
as a DMN service task (`flowable:type="dmn"`); an exclusive gateway routes on its output. The domain's
|
|
only new job is to carry the diploma origin and pass it into the process as a start variable.**
|
|
|
|
- **The decision lives in the workflow.** `workflows/diploma-eligibility.dmn` maps `diplomaOrigin`
|
|
→ `route` (`Buitenlands` ⇒ `CBGV_ADVIES`, otherwise `DIRECT`). A DMN service task
|
|
(`flowable:type="dmn"`, `decisionTableReferenceKey=diploma-eligibility`) runs it between
|
|
`OpenZaakAanmaken` and `Beoordelen`, and an exclusive gateway sends `CBGV_ADVIES` through a new
|
|
`CBGVAdvies` user task before `Beoordelen`, `DIRECT` straight there. (A `businessRuleTask` would
|
|
bind Flowable's legacy Drools/KIE implementation, which `flowable-rest` does not bundle — its parse
|
|
handler throws `NoClassDefFoundError` at deploy time; the DMN service task is the supported route.)
|
|
- **The domain carries the input, not the decision.** The `Registration` aggregate gains a
|
|
`DiplomaOrigin` (Binnenlands/Buitenlands); `SubmitRegistration` passes it to
|
|
`StartRegistrationProcessAsync`, which sets it as the `diplomaOrigin` start variable. The domain
|
|
never evaluates the DMN and never learns the route — that is the process's concern.
|
|
- **Deployed as its own DMN-engine deployment, separate from the BPMN.** The DMN is version-controlled
|
|
in `workflows/` and `flowable-init` deploys it to the DMN engine via the `dmn-api`
|
|
(`/dmn-api/dmn-repository/deployments`), while `registratie.bpmn` goes to the process engine via
|
|
`/service/repository/deployments`. Two things were learned the hard way here (both cost a CI cycle):
|
|
(1) `flowable-rest` does **not** cascade a `.dmn` bundled inside a process `.bar` into the DMN engine
|
|
— the resource is stored but no decision is created, so the service task fails at runtime with
|
|
`FlowableObjectNotFoundException: No decision found for key`; the DMN must go through `dmn-api`.
|
|
(2) Flowable's DMN XML converter rejects an XML comment placed between the `<?xml?>` declaration and
|
|
the root `<definitions>` element (`XMLStreamReader not in START_DOCUMENT or START_ELEMENT state`),
|
|
unlike its BPMN converter — so the DMN's documentation comment lives *inside* `<definitions>`.
|
|
With the decision present in the DMN repository, the process's DMN service task resolves it across
|
|
deployments by key (verified live), so no shared parent deployment id is needed.
|
|
|
|
## Consequences
|
|
|
|
**Positive**
|
|
|
|
- The eligibility rule is a first-class, inspectable workflow artefact (matching the PRD's BPMN+DMN
|
|
framing); business users can read/adjust the decision table without touching domain code.
|
|
- §8.2 stays clean: the Workflow Client remains the only code talking to Flowable, and the decision
|
|
runs inside the process the client already started — no domain→Flowable round-trip for a decision.
|
|
- The domain change is minimal and additive: one value on the aggregate, one start variable.
|
|
|
|
**Negative / costs**
|
|
|
|
- Deviates from #14's literal "evaluated by the Domain Service via Workflow Client" wording (noted on
|
|
the issue). The outcome — DMN decides eligibility, foreign diplomas get the CBGV step — is unchanged.
|
|
- The DMN and its service-task wiring are validated live (verify-domain drives a foreign
|
|
registration through CBGV-advies and a domestic one straight to beoordeling, exercising both
|
|
branches), not in unit tests — consistent with ADR-0009/0014/0015. The domain unit/acceptance tests
|
|
cover only that the origin is carried into the process.
|
|
|
|
## Alternatives considered
|
|
|
|
- **Domain evaluates the DMN via the Workflow Client** (the issue's wording). Rejected: it couples
|
|
the domain to Flowable for a decision and splits the routing across two places (domain computes,
|
|
BPMN branches), for no benefit over letting the engine that owns the process own the decision.
|
|
- **Eligibility rules in domain C#.** Rejected: it moves a governable business decision out of the
|
|
DMN the PRD calls for, and hard-codes what the reference app is meant to demonstrate as data.
|