Procest: Architecture & Data Model
1. Overview
Procest is a case management (zaakgericht werken) app for Nextcloud, built as a thin client on OpenRegister. It manages cases, tasks, statuses, roles, results, and decisions. Cases are governed by configurable case types that control behavior: allowed statuses, required fields, processing deadlines, retention rules, and more.
Architecture Pattern
┌─────────────────────────────────────────────────┐
│ Procest Frontend (Vue 2 + Pinia) │
│ - Case list/detail views │
│ - Task management │
│ - Decision tracking │
│ - My Work (werkvoorraad) dashboard │
│ - Admin settings (case types, statuses, etc.) │
└──────────────┬──────────────────────────────────┘
│ REST API calls
┌──────────────▼──────────────────────────────────┐
│ OpenRegister API │
│ /api/objects/{register}/{schema}/{id} │
│ - CRUD operations │
│ - Search, pagination, filtering │
└──────────────┬──────────────────────────────────┘
│
┌──────────────▼──────────────────────────────────┐
│ OpenRegister Storage (PostgreSQL) │
│ - JSON object storage │
│ - Schema validation │
└─────────────────────────────────────────────────┘
Procest owns no database tables. All data is stored as OpenRegister objects, defined by schemas in a dedicated register.
2. Standards Research
2.1 Standards Evaluated
| Standard | Type | Coverage | Maturity | Relevance |
|---|---|---|---|---|
| CMMN 1.1 | International (OMG) | Case plans, tasks, milestones, sentries, case file items | Mature | HIGH: designed for case management |
| BPMN 2.0 | International (OMG) | Processes, tasks, gateways, events | Very mature, widely adopted | HIGH: task/workflow modeling |
| DMN 1.x | International (OMG) | Decision tables, decision logic | Mature, growing | MEDIUM: decision modeling |
| Schema.org | International (W3C) | Action, Project, GovernmentService, Role | Very mature | MEDIUM: semantic vocabulary |
| Dublin Core (ISO 15836) | International | 15 metadata elements for documents | Very mature | LOW: document metadata |
| ZGW APIs (VNG) | Dutch gov | Zaak, Status, Resultaat, Besluit, Rol | Production, mandated NL | HIGH: Dutch API interoperability |
| RGBZ | Dutch gov | Information model behind ZGW | Stable (v1.0) | HIGH: field-level reference |
| ZGW Catalogi API | Dutch gov | ZaakType, StatusType, ResultaatType, RolType, etc. | Production (v1.3.x) | HIGH: case type system reference |
2.2 Design Principle: International First
Data storage uses international standards. Dutch government standards are an API mapping layer.
This means:
- Objects in OpenRegister are modeled after CMMN, schema.org, and BPMN concepts
- When exposing a ZGW-compatible API, we map our international objects to ZGW field names
- This makes Procest usable outside the Netherlands while remaining interoperable with Dutch systems
2.3 Key Findings
-
CMMN 1.1 (Case Management Model and Notation) is the only international standard specifically designed for case management. It defines cases, tasks, milestones, case file items, and sentries (event-condition guards). It uses a declarative model: specify what is allowed, not the exact flow.
-
BPMN 2.0 is the dominant process standard. Camunda deprecated native CMMN support in favor of implementing CMMN patterns via BPMN, citing readability. We follow this pragmatic approach.
-
ZGW/RGBZ defines the complete Dutch government case model (Zaak, Status, Resultaat, Besluit, Rol). It is production-ready and mandated for municipalities. We map to it, not build on it.
-
ZGW Catalogi API defines
ZaakTypeas a comprehensive case type system controlling: allowed statuses, result types with archival rules, role types, custom properties, required documents, processing deadlines, confidentiality defaults, and publication rules. This is the most complete case type reference available. -
Schema.org provides
Action(withactionStatus,agent,result),Project, andRole: useful as semantic annotations but not domain-specific enough for case management. -
Nextcloud provides built-in Calendar (CalDAV), activity tracking, and file management that we reuse where possible. Deck was evaluated but is not suitable (no PHP API, model doesn't fit).
3. Data Model Decisions
3.1 Standards Hierarchy
| Layer | Standard | Purpose |
|---|---|---|
| Primary (storage) | CMMN 1.1 concepts + schema.org vocabulary | International data model |
| Semantic | Schema.org JSON-LD | Type annotations for linked data |
| API mapping | ZGW/RGBZ field names | Dutch government interoperability |
| Type system reference | ZGW Catalogi API (ZaakType) | Case type behavioral controls |
| Nextcloud native | Calendar, Contacts, Files, Activity | Reuse where possible |
3.2 Entity Definitions
Case Type
A case type is a configurable definition that controls the behavior of cases: which statuses are allowed, which roles can be assigned, what properties are required, processing deadlines, and more. This is the international equivalent of ZGW's ZaakType.
| Aspect | Decision | Rationale |
|---|---|---|
| CMMN concept | CaseDefinition / CasePlanModel template | CMMN separates case definition from case instance |
| ZGW equivalent | ZaakType (Catalogi API 1.3.x) | Full behavioral case type: our primary reference |
| Versioning | Case types have validity periods and draft/published status | From ZGW: concept, validFrom, validUntil |
Core properties:
| Property | Type | CMMN / Schema.org | ZGW Mapping | Required |
|---|---|---|---|---|
title | string | schema:name | zaaktype_omschrijving | Yes |
description | string | schema:description | toelichting | No |
identifier | string | schema:identifier | identificatie | Auto |
purpose | string | : | doel | Yes |
trigger | string | : | aanleiding | Yes |
subject | string | : | onderwerp | Yes |
initiatorAction | string | : | handeling_initiator | Yes |
handlerAction | string | : | handeling_behandelaar | Yes |
origin | enum: internal, external | : | indicatie_intern_of_extern | Yes |
processingDeadline | duration (ISO 8601) | CMMN TimerEventListener | doorlooptijd_behandeling | Yes |
serviceTarget | duration (ISO 8601) | : | servicenorm_behandeling | No |
suspensionAllowed | boolean | : | opschorting_en_aanhouding_mogelijk | Yes |
extensionAllowed | boolean | : | verlenging_mogelijk | Yes |
extensionPeriod | duration (ISO 8601) | : | verlengingstermijn | Conditional |
confidentiality | enum | : | vertrouwelijkheidaanduiding | Yes |
publicationRequired | boolean | : | publicatie_indicatie | Yes |
publicationText | string | : | publicatietekst | No |
responsibleUnit | string | : | verantwoordelijke | Yes |
referenceProcess | string | : | referentieproces_naam | No |
isDraft | boolean | : | concept | No (default: true) |
validFrom | date | : | datum_begin_geldigheid | Yes |
validUntil | date | : | datum_einde_geldigheid | No |
keywords | string[] | : | trefwoorden | No |
subCaseTypes | reference[] | CMMN CaseTask | deelzaaktypen | No |
Confidentiality levels (from ZGW, internationally applicable):
| Level | ZGW Dutch | Description |
|---|---|---|
public | openbaar | Publicly accessible |
restricted | beperkt_openbaar | Restricted public access |
internal | intern | Internal use only |
case_sensitive | zaakvertrouwelijk | Case-confidential |
confidential | vertrouwelijk | Confidential |
highly_confidential | confidentieel | Highly confidential |
secret | geheim | Secret |
top_secret | zeer_geheim | Top secret |
Status Type
A configurable status definition linked to a case type. Controls which lifecycle phases a case can go through.
| Aspect | Decision | Rationale |
|---|---|---|
| CMMN concept | Milestone + PlanItem states | Milestones mark case progression |
| Schema.org type | schema:ActionStatusType | Standard status enumeration |
| ZGW equivalent | StatusType (Catalogi API) | Per-case-type status definitions |
Core properties:
| Property | Type | Source | ZGW Mapping | Required |
|---|---|---|---|---|
name | string | schema:name | statustype_omschrijving | Yes |
description | string | schema:description | toelichting | No |
caseType | reference | Parent case type | zaaktype | Yes |
order | integer (1–9999) | CMMN Milestone sequence | statustypevolgnummer | Yes |
isFinal | boolean | CMMN terminal state | (last in order) | No (default: false) |
targetDuration | duration | : | doorlooptijd | No |
notifyInitiator | boolean | : | informeren | No (default: false) |
notificationText | string | : | statustekst | No |
Result Type
A configurable result definition linked to a case type. Controls which outcomes are possible and how they affect archival.
| Aspect | Decision | Rationale |
|---|---|---|
| CMMN concept | Case outcome | Case completion result type |
| ZGW equivalent | ResultaatType (Catalogi API) | Result type with archival rules |
Core properties:
| Property | Type | Source | ZGW Mapping | Required |
|---|---|---|---|---|
name | string | schema:name | omschrijving | Yes |
description | string | schema:description | toelichting | No |
caseType | reference | Parent case type | zaaktype | Yes |
archiveAction | enum: retain, destroy | : | archiefnominatie | No |
retentionPeriod | duration (ISO 8601) | : | archiefactietermijn | No |
retentionDateSource | enum | : | afleidingswijze | No |
Retention date source values (from ZGW, internationally applicable):
| Value | ZGW Dutch | Description |
|---|---|---|
case_completed | afgehandeld | Case completion date |
decision_effective | ingangsdatum_besluit | Decision effective date |
decision_expiry | vervaldatum_besluit | Decision expiry date |
fixed_period | termijn | Case completion + fixed period |
related_case | gerelateerde_zaak | Related case completion |
parent_case | hoofdzaak | Parent case completion |
custom_property | eigenschap | Value of a case property |
custom_date | ander_datumkenmerk | Manually determined date |
Role Type
A configurable role definition linked to a case type. Controls which participant roles can be assigned.
| Aspect | Decision | Rationale |
|---|---|---|
| Schema.org type | schema:Role | Standard role qualification |
| ZGW equivalent | RolType (Catalogi API) | Per-case-type role definitions |
Core properties:
| Property | Type | Source | ZGW Mapping | Required |
|---|---|---|---|---|
name | string | schema:roleName | omschrijving | Yes |
caseType | reference | Parent case type | zaaktype | Yes |
genericRole | enum | : | omschrijvingGeneriek | Yes |
Standard generic roles (from ZGW, internationally applicable):
| Role | ZGW Dutch | Description |
|---|---|---|
initiator | Initiator | Started the case |
handler | Behandelaar | Processes the case |
advisor | Adviseur | Provides advice |
decision_maker | Beslisser | Makes decisions |
stakeholder | Belanghebbende | Has interest in outcome |
coordinator | Zaakcoördinator | Coordinates the case |
contact | Klantcontacter | Contact person |
co_initiator | Mede-initiator | Co-initiator |
Property Definition
A configurable custom field definition linked to a case type. Controls which additional data fields cases of this type must capture.
| Aspect | Decision | Rationale |
|---|---|---|
| Schema.org type | schema:PropertyValueSpecification | Defines expected property values |
| ZGW equivalent | Eigenschap (Catalogi API) | Case-type-specific custom properties |
Core properties:
| Property | Type | Source | ZGW Mapping | Required |
|---|---|---|---|---|
name | string | schema:name | eigenschapnaam | Yes |
definition | string | schema:description | definitie | Yes |
caseType | reference | Parent case type | zaaktype | Yes |
format | enum: text, number, date, datetime | : | formaat | Yes |
maxLength | integer | : | lengte | No |
allowedValues | string[] | : | waardenverzameling | No |
requiredAtStatus | reference | Status at which this must be filled | statustype | No |
Document Type
A configurable document type definition linked to a case type. Controls which document types are expected.
| Aspect | Decision | Rationale |
|---|---|---|
| Schema.org type | schema:DigitalDocument | Document type definition |
| ZGW equivalent | InformatieObjectType + ZaakTypeInformatieObjectType | Document type requirements |
Core properties:
| Property | Type | Source | ZGW Mapping | Required |
|---|---|---|---|---|
name | string | schema:name | omschrijving | Yes |
category | string | : | informatieobjectcategorie | Yes |
caseType | reference | Parent case type | zaaktype (via through table) | Yes |
direction | enum: incoming, internal, outgoing | : | richting | Yes |
order | integer | : | volgnummer | Yes |
confidentiality | enum | : | vertrouwelijkheidaanduiding | No |
requiredAtStatus | reference | Status requiring this document | statustype | No |
Decision Type
A configurable decision type definition linked to a case type.
| Aspect | Decision | Rationale |
|---|---|---|
| Schema.org type | schema:ChooseAction definition | Decision type specification |
| ZGW equivalent | BesluitType (Catalogi API) | Administrative decision types |
Core properties:
| Property | Type | Source | ZGW Mapping | Required |
|---|---|---|---|---|
name | string | schema:name | omschrijving | Yes |
description | string | schema:description | toelichting | No |
category | string | : | besluitcategorie | No |
objectionPeriod | duration (ISO 8601) | : | reactietermijn | No |
publicationRequired | boolean | : | publicatie_indicatie | Yes |
publicationPeriod | duration (ISO 8601) | : | publicatietermijn | No |
Case (Zaak)
A case is a coherent body of work with a defined lifecycle, initiation, and result. Cases are governed by a case type.
| Aspect | Decision | Rationale |
|---|---|---|
| CMMN concept | CasePlanModel / Case instance | CMMN's core concept: a case with a plan |
| Schema.org type | schema:Project | "An enterprise planned to achieve a particular aim" |
| ZGW mapping | Zaak | Direct mapping for Dutch API compatibility |
Core properties (international → ZGW mapping):
| Property | Type | CMMN/Schema.org Source | ZGW Mapping | Required |
|---|---|---|---|---|
title | string | schema:name | omschrijving | Yes |
description | string | schema:description | toelichting | No |
identifier | string | schema:identifier | identificatie | Auto |
caseType | reference | CMMN CaseDefinition | zaaktype | Yes |
status | reference | CMMN PlanItem lifecycle | status (URL ref) | Yes |
result | reference | CMMN case outcome | resultaat (URL ref) | No |
startDate | date | schema:startDate | startdatum | Yes |
endDate | date | schema:endDate | einddatum | No |
plannedEndDate | date | : | einddatumGepland | No |
deadline | date | : | uiterlijkeEinddatumAfdoening | Auto (from caseType) |
confidentiality | enum | : | vertrouwelijkheidaanduiding | No (default from caseType) |
assignee | string | CMMN HumanTask.assignee | : | No |
priority | enum | schema:priority | : | No |
parentCase | reference | CMMN CaseTask (sub-case) | hoofdzaak | No |
relatedCases | array | : | relevanteAndereZaken | No |
geometry | GeoJSON | schema:geo | zaakgeometrie | No |
Case type behavioral controls on cases:
deadlineis auto-calculated:startDate+caseType.processingDeadlineconfidentialitydefaults fromcaseType.confidentiality- Only status types linked to the case type are allowed
- Only role types linked to the case type are allowed
- Property definitions linked to the case type must be satisfied before reaching required statuses
- Document types linked to the case type define which documents are expected
Task
A work item within a case.
| Aspect | Decision | Rationale |
|---|---|---|
| CMMN concept | HumanTask | CMMN's primary task type |
| Schema.org type | schema:Action | With actionStatus for lifecycle |
Core properties:
| Property | Type | CMMN/Schema.org Source | Required |
|---|---|---|---|
title | string | schema:name | Yes |
description | string | schema:description | No |
status | enum | CMMN PlanItem states: available, active, completed, terminated | Yes |
assignee | string | CMMN assignee | No |
case | reference | CMMN parent case | Yes |
dueDate | datetime | schema:endTime | No |
priority | enum | schema:priority | No |
completedDate | datetime | schema:endTime | No |
Task status values (from CMMN PlanItem lifecycle):
| Status | CMMN State | Description |
|---|---|---|
available | Available | Task can be started |
active | Active | Task is being worked on |
completed | Completed | Task finished successfully |
terminated | Terminated | Task stopped before completion |
disabled | Disabled | Task not applicable |
Role (Rol)
The relationship between a person/organization and a case.
Core properties:
| Property | Type | Schema.org Source | ZGW Mapping | Required |
|---|---|---|---|---|
name | string | schema:roleName | omschrijving | Yes |
description | string | schema:description | roltoelichting | No |
roleType | reference | : | omschrijvingGeneriek (via RoleType) | Yes |
case | reference | : | zaak | Yes |
participant | string (user UID or contact ref) | schema:agent | betrokkene | Yes |
Result (Resultaat)
The outcome of a case.
Core properties:
| Property | Type | Source | Required |
|---|---|---|---|
name | string | schema:name | Yes |
description | string | schema:description | No |
case | reference | Parent case | Yes |
resultType | reference | ResultType definition | Yes |
Decision (Besluit)
A formal decision made on a case.
Core properties:
| Property | Type | Schema.org Source | ZGW Mapping | Required |
|---|---|---|---|---|
title | string | schema:name | : | Yes |
description | string | schema:description | toelichting | No |
case | reference | : | zaak | No |
decisionType | reference | : | besluittype | No |
decidedBy | string | schema:agent | : | No |
decidedAt | datetime | schema:endTime | datum | No |
effectiveDate | date | schema:startTime | ingangsdatum | No |
expiryDate | date | schema:endTime | vervaldatum | No |
3.3 Case Type System Overview
The case type system forms a hierarchy where the CaseType is the central configuration entity controlling all related type definitions:
CaseType
├── StatusType[] : Allowed lifecycle phases (ordered)
├── ResultType[] : Allowed outcomes (with archival rules)
├── RoleType[] : Allowed participant roles
├── PropertyDefinition[]: Required custom data fields
├── DocumentType[] : Required document types
├── DecisionType[] : Allowed decision types
└── subCaseTypes[] : Allowed sub-case types
Behavioral controls:
| Control | How | ZGW Reference |
|---|---|---|
| Allowed statuses | StatusTypes linked to CaseType; ordered by order | StatusType → ZaakType FK |
| Status transitions | Only sequential progression through ordered statuses | statustypevolgnummer sequence |
| Required fields per status | PropertyDefinition with requiredAtStatus reference | Eigenschap → StatusType FK |
| Required documents per status | DocumentType with requiredAtStatus reference | ZaakTypeInformatieObjectType → StatusType FK |
| Processing deadline | Auto-calculated: case.startDate + caseType.processingDeadline | doorlooptijd_behandeling |
| Suspension/extension | Controlled by suspensionAllowed and extensionAllowed | opschorting_en_aanhouding_mogelijk, verlenging_mogelijk |
| Confidentiality default | Cases inherit caseType.confidentiality | vertrouwelijkheidaanduiding |
| Archival per result | ResultType defines archival action and retention period | archiefnominatie, archiefactietermijn |
| Draft/published lifecycle | CaseType has isDraft; draft types cannot create cases | concept |
| Validity window | CaseType has validFrom / validUntil | datum_begin_geldigheid, datum_einde_geldigheid |
3.4 My Work (Werkvoorraad)
A cross-entity workload view showing all items assigned to the current user. No new entity is needed: this is a frontend aggregation pattern.
How it works:
- Query cases with
assignee == currentUserand non-final status - Query tasks with
assignee == currentUserand statusavailableoractive - Optionally include leads and requests from Pipelinq (
assignedTo == currentUser) - Merge, sort by priority then due date, display as unified card list
Required fields for My Work (already present on Case, Task, and Pipelinq entities):
| Field | Case | Task | Pipelinq Lead | Pipelinq Request |
|---|---|---|---|---|
assignee / assignedTo | Yes | Yes | Yes | Yes |
priority | Yes | Yes | Yes | Yes |
deadline / dueDate | Yes | Yes | Yes | : |
status | Yes | Yes | (via stage) | Yes |
| Entity type label | "Case" | "Task" | "Lead" | "Request" |
3.5 Relationship to Pipelinq
Procest receives cases from Pipelinq through the request-to-case (verzoek-to-zaak) flow:
Pipelinq (CRM) Procest (Case Management)
┌──────────────┐ ┌──────────────┐
│ Client │ │ Case │
│ Contact │──── Request ────>│ Task │
│ Lead │ (verzoek) │ Status │
│ Pipeline │ │ Role │
└──────────────┘ │ Result │
│ Decision │
│ CaseType │
└──────────────┘
When a Pipelinq Request is converted to a Case:
- The requesting client is linked as a
Role(type:initiator) on the case - The request description becomes the case description
- The request category informs the case type selection
- The case type determines initial status, deadline, and required fields