Wanneer een wijziging in configuratie of bedrijfsgegevens gevolgen kan hebben voor andere gebruikers, kan een applicatie vereisen dat de ene persoon de wijziging voorstelt en een andere deze beoordeelt voordat ze van kracht wordt. Zo’n proces ontwerpen als een goedkeuringsworkflow voor wijzigingen in een PHP-SaaS vraagt om meer dan alleen een knop ‘Goedkeuren’ toevoegen: je moet het voorstel scheiden van de huidige gegevens, elke transitie beveiligen en vastleggen wat er gebeurt als de context verandert terwijl het verzoek wacht.
Het doel is dat het systeem controleerbaar kan beantwoorden wat er is aangevraagd, wie het heeft beoordeeld, met welke machtigingen en of het daadwerkelijk is toegepast. De oplossing moet in verhouding staan tot het risico: een onnodige workflow zorgt voor extra frictie, terwijl een ontoereikende workflow onjuiste of moeilijk traceerbare wijzigingen mogelijk kan maken.
Bepalen voor welke wijzigingen goedkeuring nodig is

Begin met het classificeren van wijzigingen op basis van impact, bereik en herstelbaarheid. Niet elk bewerkbaar gegeven hoeft te worden beoordeeld. Een gebruiker die een persoonlijke voorkeur met weinig impact wijzigt, kan de wijziging rechtstreeks opslaan. Daarentegen kan het redelijk zijn om goedkeuring te eisen voor het aanpassen van een factureringsregel, een configuratie die door een organisatie wordt gedeeld of stamgegevens die meerdere processen beïnvloeden.
Leg expliciete productcriteria vast. Vraag bijvoorbeeld of de wijziging gevolgen heeft voor meer gebruikers dan alleen degene die haar voorstelt, of ze bedragen of machtigingen kan wijzigen, of intern beleid een tweede persoon vereist en hoeveel het kost om de wijziging te corrigeren. Documenteer ook de uitzonderingen en wie deze mag aanpassen. Maak van goedkeuring geen algemeen mechanisme voor bewerkingen die niets met deze situatie te maken hebben: de scope hier omvat wijzigingen in configuratie of bedrijfsgegevens.
De beslissing moet in de serverlogica worden afgedwongen, niet alleen in de interface. Een knop verbergen voorkomt niet dat iemand een endpoint rechtstreeks aanroept. De API moet bepalen of voor het type wijziging goedkeuring nodig is en die regel toepassen op zowel afzonderlijke bewerkingen als bulkacties en integraties die dezelfde gegevens kunnen wijzigen.
Het verzoek scheiden van de actuele gegevens
Overschrijf de actuele gegevens niet wanneer je een voorstel aanmaakt. Sla een afzonderlijk verzoek op met ten minste de organisatie, de betreffende resource, de voorgestelde waarden, de status, wie het heeft aangemaakt en wanneer. Afhankelijk van het product kan het ook nodig zijn om een reden, de herkomst van het verzoek, een deadline of een versie van de oorspronkelijke gegevens op te slaan.
Je kunt de waarden opslaan in getypeerde velden of in een door de server gevalideerde geserialiseerde structuur. Accepteer in beide gevallen geen willekeurige payload alsof het een rechtstreekse update is. Valideer bij ontvangst van het voorstel het schema, de toegestane velden, de indelingen en de bedrijfsregels. Valideer bij het toepassen opnieuw wat van toepassing is, omdat de omstandigheden tijdens het wachten kunnen zijn veranderd.
Met dit ontwerp kun je de actuele en voorgestelde waarde vergelijken zonder de ervaring van andere gebruikers te beïnvloeden. Het maakt het ook mogelijk om in het beoordelingsscherm een begrijpelijk verschil te tonen. Presenteer een waarde die nog in behandeling is niet als actueel; maak duidelijk welke gegevens zijn toegepast en welke alleen zijn voorgesteld.
Expliciete statussen en transities modelleren
Definieer een kleine set statussen met een precieze betekenis. Een gebruikelijke reeks kan bestaan uit proposed (voorgesteld), in_review (in beoordeling), approved (goedgekeurd), rejected (afgewezen), expired (verlopen) en applied (toegepast). Niet elk product heeft de status voor beoordeling nodig: als een onmiddellijke beslissing volstaat, kan die status achterwege blijven. Het belangrijkste is dat elke status een operationele situatie beschrijft en geen decoratief label is.
Leg de geldige transities vast. Een voorstel kan bijvoorbeeld ter beoordeling worden voorgelegd of worden afgewezen; een verzoek in beoordeling kan worden goedgekeurd of afgewezen; en een goedkeuring kan worden toegepast zolang ze nog geldig is. Een verlopen verzoek mag niet via dezelfde route als een actief verzoek worden goedgekeurd. Niet-toegestane acties moeten een consistente foutmelding opleveren, ook als de interface die optie niet aanbiedt.
‘Goedgekeurd’ en ‘toegepast’ zijn niet hetzelfde. De goedkeuring registreert een beslissing; de toepassing wijzigt de actuele gegevens. Door beide stappen apart te houden, kun je fouten bij het toepassen opsporen en goedgekeurde verzoeken tonen die nog aandacht nodig hebben. Als het proces binnen één korte transactie kan worden afgerond, kunnen de beslissing en de schrijfactie op elkaar worden afgestemd, maar het model moet nog steeds onderscheiden wat er is gebeurd.
Machtigingen bij elke transitie controleren
Autoriseer elke actie op de server op basis van de organisatie, de rol, de resource en de huidige status. Ga er niet van uit dat toestemming om een verzoek aan te maken ook toestemming geeft om het goed te keuren of toe te passen. Vertrouw evenmin op de organisatie-ID die de client verstuurt: leid de scope af uit een gevalideerde sessie of credential en controleer of het verzoek en de resource binnen dezelfde scope vallen.
Als zelfgoedkeuring verboden is, controleer dan of de identiteit van de goedkeurder verschilt van die van degene die het voorstel heeft ingediend. Deze regel moet tijdens de transitie worden afgedwongen, niet alleen in de weergave. Bepaal ook wat er gebeurt met verzoeken van beheerders, servicegebruikers en geautomatiseerde processen; uitzonderingen vereisen een bewust beleid en mogen niet het gevolg zijn van een onbedoelde omissie.
Een goedkeuring moet gekoppeld blijven aan een specifiek voorstel. Als de inhoud ervan verandert, is de eerdere goedkeuring niet langer geldig: maak een nieuwe versie van het verzoek aan of zet het terug naar een status waarin opnieuw beoordeling nodig is. Waarden na goedkeuring stilzwijgend wijzigen verbreekt de overeenkomst tussen wat is beoordeeld en wat is toegepast.
Concurrency, verlopen verzoeken en toepassing beheren
Terwijl een verzoek wacht, kan een andere actor de oorspronkelijke gegevens wijzigen. Registreer daarom bij het aanmaken van het voorstel de versie van de resource of de relevante waarden. Vergelijk bij het toepassen de verwachte versie met de actuele versie. Als die niet overeenkomen, overschrijf de nieuwe wijziging dan niet zonder beleid: blokkeer de toepassing, markeer een conflict of vraag om een nieuwe beoordeling, afhankelijk van de productregels.
In een relationele database kan een transactie de uitlezing van het verzoek, de controle van de status en de update van de resource beschermen. Het specifieke patroon hangt af van de database-engine en het niveau van concurrency; pessimistische locking of een conditionele update op basis van de versie kan passend zijn. De transitie moet in elk geval atomair zijn: twee gelijktijdige verzoeken mogen dezelfde wijziging niet allebei toepassen.
Maak de toepassing idempotent of wijs retries veilig af. Als je gegevens in de database bijwerkt en daarnaast een melding verstuurt of een event publiceert, overweeg dan om het te verwerken event in dezelfde transactie vast te leggen en het daarna te verwerken. Zo verklein je het risico dat een succesmelding wordt getoond voor een schrijfactie die niet is bevestigd.
Definieer ook een vervalbeleid: hoe lang een voorstel actief mag blijven, waardoor het verloopt en of het kan worden verlengd. Controleer het verlopen op de server bij het goedkeuren en toepassen; alleen de status wijzigen met een geplande taak is niet voldoende. Als een vertraging ervoor zorgt dat de waarden of omstandigheden niet langer betrouwbaar zijn, moet een nieuw voorstel worden ingediend.
De workflow auditen en beheren
Bewaar een registratie van wie elke wijziging heeft voorgesteld, goedgekeurd, afgewezen en toegepast, met tijdstippen, organisatie- en resource-ID’s, het resultaat en de context die nodig is voor onderzoek. Sla een verwijzing naar de voorgestelde versie en de beslissing op. Het auditlog mag niet kunnen worden gewijzigd via de normale functies voor het bewerken van verzoeken. Beperk de geregistreerde gegevens tot wat nodig is en pas het toegangs- en bewaarbeleid van het product toe.
De operationele interface moet helpen bij het vinden van openstaande, verlopen of door conflicten geblokkeerde verzoeken, en van goedgekeurde maar nog niet toegepaste verzoeken. Licht de reden voor de blokkering toe en bied een veilige manier om die op te lossen; zet een fout bij het toepassen niet om in automatische goedkeuring. Meldingen kunnen gebruikers op nieuwe taken en beslissingen attenderen, maar zijn slechts een hulpmiddel: de persistente en opvraagbare status is de bron van waarheid.
Invarianten, machtigingen en fouten testen

De tests moeten de regels controleren die nooit mogen worden geschonden. Een niet-goedgekeurd verzoek wijzigt de actuele gegevens niet; iemand zonder toegang tot de organisatie kan het verzoek niet lezen of naar een andere status overzetten; zelfgoedkeuring mislukt als die verboden is; en een goedgekeurd verzoek kan niet worden toegepast met andere inhoud dan de beoordeelde inhoud. Voeg tests toe voor terminale statussen, verlopen verzoeken en ongeldige transities.
Test ook races tussen twee goedkeuringen of tussen een goedkeuring en een wijziging van de oorspronkelijke gegevens. Simuleer fouten tijdens het toepassen en controleer dat het verzoek niet als toegepast wordt gemarkeerd als de schrijfactie niet is bevestigd. Als er een taak bestaat die verzoeken laat verlopen, controleer dan of herhaalde uitvoering veilig is. Deze tests maken van bedrijfsregels controleerbare garanties en helpen de workflow te onderhouden wanneer machtigingen of het gegevensmodel veranderen.
Als ontwerpcriterium moet elke fase een verantwoordelijke, een verifieerbare voorwaarde en een waarneembaar resultaat hebben. Als het team niet kan uitleggen wie toestemming heeft voor elke transitie, wat er gebeurt bij een gelijktijdige wijziging of hoe een beslissing kan worden gereconstrueerd, moet de workflow verder worden uitgewerkt voordat die naar productie gaat.



