AutoMate Technisch Help

OrgModel Versioning

Concept

Een OrgModel-versie legt een genummerde momentopname vast van alle gebruikersattributen die bij een tenant horen. Per tenant bestaan er op elk moment maximaal twee versies tegelijk:

  • Actieve versie (IsActive = true, IsDraft = false) — de snapshot die downstream systemen als bron van waarheid gebruiken.

  • Concept (IsDraft = true, IsActive = false) — een bewerkbare werkversie waaraan attributen worden toegevoegd, hernoemd of verwijderd.

Wijzigingen worden incrementeel opgeslagen als changerecords in het concept. Zodra het concept wordt geactiveerd, promoot de service het atomisch naar actief: de vorige actieve versie wordt gedeactiveerd, de changerecords worden geconsolideerd tot een volledige snapshot en downstream systemen kunnen de nieuwe versie inlezen.

Er is altijd hooguit één open concept tegelijk. Een tweede CreateDraft-aanroep terwijl er al een concept bestaat, retourneert 409 DraftAlreadyExists.

Lifecycle-toestandsdiagram

POST /versions/draft (CreateDraft)

attributen toevoegen / wijzigen / verwijderen

GET /versions/draft/impact

CanActivate = false\n(blocking issues)

CanActivate = true

DELETE /versions/draft (ResetDraft)\nsoft-delete pending-add attributen

POST /versions/draft/activate\nAtomische promotie + snapshot

nieuw concept aanmaken\nvoor volgende versie

Concept

ImpactEvaluatie

Activatie

Actief

Endpoints overzicht

Alle paden zijn relatief aan de basisroute GET /usertypes/org-model/versions. De tenant-ID wordt altijd uit de X-Tenant-Id header gehaald via de MultiTenantService.

Methode

Pad

Beschrijving

Succesrespons

GET

/active/attributes

Geeft alle attributen van de huidige actieve versie-snapshot terug.

200 OK

GET

/draft/changes

Geeft de openstaande changerecords van het huidige concept terug.

200 OK

GET

/{versionId}/changes

Geeft de changerecords van een specifieke (historische) versie terug.

200 OK/404

GET

/draft/impact

Evalueert de downstreamimpact van activatie van het huidige concept.

200 OK

POST

/draft/activate

Activeert het huidige concept (hervoert impact; blokkeert bij openstaande issues).

204 No Content

POST

/draft/resolutions

Dient resolutiekeuzes in voor blokkerende impactproblemen.

201 Created

DELETE

/draft

Reset (gooit weg) het huidige concept.

204 No Content

Draft wijzigingen

Elke mutatie op een attribuut terwijl een concept open is, schrijft een changerecord met een van de volgende typen:

ChangeType

Betekenis

Add

Een nieuw attribuut is aangemaakt en wacht op activatie.

Remove

Een bestaand attribuut is verwijderd uit het model.

Rename

Een attribuut heeft een nieuwe naam gekregen (NewName gevuld).

GetDraftChangesAsync vs GetVersionChangesAsync

Beide methoden lezen uit dezelfde changerecordstabel maar richten zich op een andere versie:

  • GetDraftChangesAsync — zoekt eerst het openstaande concept op voor de huidige tenant en leest de changerecords van dat concept. Geeft een lege lijst terug als er geen concept bestaat.

  • GetVersionChangesAsync(versionId) — leest de changerecords van een willekeurige (historische) versie op basis van ID. Geeft null terug als de versie niet bestaat of niet bij de tenant hoort.

Het DTO dat beide methoden teruggeven is identiek (OrgModelVersionChangeDto):

public sealed class OrgModelVersionChangeDto { public Guid AttributeId { get; set; } public string AttributeName { get; set; } public int AttributeTypeId { get; set; } public string ChangeType { get; set; } // "Add" | "Remove" | "Rename" public string? NewName { get; set; } // alleen bij Rename public DateTime ChangedAt { get; set; } }

Actieve attributen

GetActiveAttributesAsync bepaalt welke actieve versie bij de huidige tenant hoort, haalt de bijbehorende versie-snapshot op en geeft alle attributen terug als OrgModelAttributeDto. Als er nog geen actieve versie bestaat, geeft de methode een lege collectie terug — nooit een fout.

public sealed class OrgModelAttributeDto { public Guid Id { get; set; } public int TypeId { get; set; } public string Name { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } }

Impact evaluatie

EvaluateDraftImpactAsync voert een pipeline uit van alle geregistreerde IOrgModelImpactEvaluator-implementaties. Dit maakt het mogelijk om downstream effecten van attributenwijzigingen te beoordelen voordat activatie plaatsvindt.

Werking

  1. De service haalt het openstaande concept op voor de huidige tenant.

  2. Alle changerecords van het concept worden geladen.

  3. De service laadt de completed resolution keys uit de database: een verzameling sleutels in het formaat "{IssueCode}:{AffectedEntityId ?? string.Empty}" voor problemen die al succesvol zijn opgelost.

  4. Een OrgModelImpactContext-object wordt aangemaakt met de concept- en changedata plus de completed keys.

  5. Elke evaluator roept EvaluateAsync(context, ct) aan en voegt eventuele bevindingen toe aan de context.

  6. Na afloop worden openstaande blokkerende problemen verrijkt met hun ingeleverde resolutierecord (indien aanwezig).

  7. Het eindresultaat is een DraftImpactResult.

OrgModelImpactContext

Eigenschap

Type

Beschrijving

TenantId

Guid

Tenant waarvoor de evaluatie wordt uitgevoerd.

Draft

OrgModelVersionRecord

Het huidige conceptrecord.

Changes

IReadOnlyList<OrgModelVersionChangeDto>

Alle openstaande changerecords.

CompletedResolutionKeys

IReadOnlySet<string>

Sleutels van problemen met een Completed-resolutie.

BlockingIssues

List<OrgModelImpactIssue>

Problemen die activatie blokkeren (evaluators voegen hier aan toe).

Warnings

List<OrgModelImpactIssue>

Informatieve problemen die activatie niet blokkeren.

AffectedSystems

List<OrgModelAffectedSystem>

Downstream systemen die door activatie worden geraakt.

Evaluators gebruiken context.IsResolved(code, entityId) om te controleren of een probleem al is opgelost en kunnen het in dat geval overslaan:

public async Task EvaluateAsync(OrgModelImpactContext context, CancellationToken ct) { foreach (var change in context.Changes.Where(c => c.ChangeType is "Remove" or "Rename")) { if (context.IsResolved("persona.attribute_in_use", change.AttributeId.ToString())) continue; // al opgelost via een resolutie // ... evaluatielogica context.BlockingIssues.Add(new OrgModelImpactIssue { Code = "persona.attribute_in_use", Description = $"Attribuut '{change.AttributeName}' wordt gebruikt in een persona-filter.", AffectedEntityId = change.AttributeId.ToString(), ResolutionActions = [ /* beschikbare acties */ ] }); } }

OrgModelImpactIssue

Eigenschap

Type

Beschrijving

Code

string

Machine-leesbare code, bijv. persona.attribute_in_use.

Description

string

Mensleesbare uitleg die in de UI wordt getoond.

AffectedEntityId

string?

Optioneel ID van het betrokken object (bijv. persona-ID).

ResolutionActions

IReadOnlyList<OrgModelResolutionAction>

Beschikbare resolutieacties voor dit probleem.

Resolution

OrgModelDraftResolutionRecord?

Ingeleverde resolutie (gevuld door de service, niet door evaluators).

DraftImpactResult

Eigenschap

Type

Beschrijving

CanActivate

bool

true als er geen blokkerende problemen zijn.

PendingChangeCount

int

Aantal openstaande changerecords in het concept.

BlockingIssues

IReadOnlyList<OrgModelImpactIssue>

Problemen die activatie blokkeren.

Warnings

IReadOnlyList<OrgModelImpactIssue>

Informatieve problemen.

AffectedSystems

IReadOnlyList<OrgModelAffectedSystem>

Betrokken downstream systemen.

Als er geen concept bestaat, retourneert de methode altijd CanActivate = false met een enkel blokkerend probleem orgmodel.version.no_draft_to_evaluate. Zie OrgModel-Draft-Resolutions.md voor details over hoe resoluties blokkerende problemen onderdrukken.

Activatie guard

ActivateDraftAsync voert de impact-evaluatie altijd opnieuw uit, ook als de aanroeper GET /draft/impact al eerder heeft gebruikt. Dit voorkomt race conditions waarbij de toestand verandert tussen evaluatie en activatie.

Nee

Ja

Nee

Ja

POST /draft/activate

Concept aanwezig?

422 NoDraftToActivate

EvaluateDraftImpactAsync

CanActivate?

422 BlockingIssuesNotResolved

ActivateDraftAsync in repository

ReconcileAndSnapshotAsync

204 No Content

Als CanActivate = false, gooit de service een OrgModelDomainException met foutcode orgmodel.version.blocking_issues_not_resolved en HTTP-status 422.

Draft resetten

ResetDraftAsync gooit het huidige concept weg zonder het te activeren:

  1. Controleer of er een open concept bestaat; zo niet, gooi 422 NoDraftToReset.

  2. Haal alle attribuut-ID's op van changerecords van het type Add in dit concept (attributen die nog nooit actief zijn geweest).

  3. Verwijder deze attributen soft uit de attributentabel (SoftDeleteBatchAsync), zodat ze niet als "wees"-records achterblijven.

  4. Reset het conceptrecord in de versietabel (ResetDraftAsync in de repository): het concept blijft in de database staan voor auditdoeleinden maar is niet langer bewerkbaar.

Na de reset kan een nieuw concept worden aangemaakt via POST /versions/draft.

Foutcodes

Foutcode

HTTP-status

Situatie

orgmodel.version.draft_already_exists

409

Er bestaat al een open concept voor deze tenant.

orgmodel.version.no_draft_to_activate

422

Er is geen open concept om te activeren.

orgmodel.version.no_draft_to_reset

422

Er is geen open concept om te resetten.

orgmodel.version.no_draft_to_evaluate

n.v.t.

Geen concept aanwezig bij impact-evaluatie (in response-body).

orgmodel.version.blocking_issues_not_resolved

422

Activatie geblokkeerd omdat er nog blokkerende problemen zijn.

orgmodel.version.no_draft_to_submit_resolutions

422

Er is geen open concept waaraan resoluties gekoppeld kunnen worden.

orgmodel.attribute.referenced_by_version

409

Een attribuut kan niet worden verwijderd omdat het aan een versie gekoppeld is.

orgmodel.attribute.invalid_type_id

422

Het opgegeven type-ID bestaat niet.

orgmodel.attribute.name_required

422

Attribuutnaam is verplicht.

orgmodel.attribute.name_too_long

422

Attribuutnaam overschrijdt de maximumlengte.

orgmodel.attribute.invalid_id

422

Het opgegeven attribuut-ID is ongeldig.

Nieuwe impact evaluator toevoegen

Een evaluator is een klasse die IOrgModelImpactEvaluator implementeert:

public interface IOrgModelImpactEvaluator { Task EvaluateAsync(OrgModelImpactContext context, CancellationToken ct); }

Stap 1: Evaluator implementeren

internal sealed class WorkflowAttributeImpactEvaluator( IWorkflowRepository workflows) : IOrgModelImpactEvaluator { public async Task EvaluateAsync(OrgModelImpactContext context, CancellationToken ct) { foreach (var change in context.Changes.Where(c => c.ChangeType == "Remove")) { if (context.IsResolved("workflow.attribute_in_use", change.AttributeId.ToString())) continue; var affectedWorkflows = await workflows.FindByAttributeIdAsync( context.TenantId, change.AttributeId, ct); foreach (var wf in affectedWorkflows) { context.BlockingIssues.Add(new OrgModelImpactIssue { Code = "workflow.attribute_in_use", Description = $"Workflow '{wf.Name}' gebruikt attribuut '{change.AttributeName}'.", AffectedEntityId = wf.Id.ToString(), ResolutionActions = [ new OrgModelResolutionAction { ActionType = "workflow.remove_attribute_reference", Label = $"Verwijder attribuutverwijzing uit workflow '{wf.Name}'" } ] }); } } } }

Stap 2: Registreer in DI

services.AddScoped<IOrgModelImpactEvaluator, WorkflowAttributeImpactEvaluator>();

De OrgModelVersionsService ontvangt via constructor-injectie een IEnumerable<IOrgModelImpactEvaluator> en roept elke evaluator aan. Er zijn geen andere wijzigingen nodig.

Gerelateerde pagina's

  • OrgModel-Draft-Resolutions.md — Hoe resolutiekeuzes worden ingediend, verwerkt als Hangfire-jobs en geblokkeerde activatie deblokkeren.

  • OrgModel-Portal-UI.md — Paginabeheer, paginavergrendeling (SignalR), attribuutbeheer en conceptbeheer in de ManagementPortal.

Last modified: 23 March 2026