Forretningslogik og stabil drift
Status og arbejdsgange: stop ugyldige spring i appen
Et statusfelt er først pålideligt, når appen ved, hvilke skift der er tilladt, hvem der må udføre dem, hvilke betingelser der skal være opfyldt, og hvad der skal ske bagefter. Ellers kan en bruger eller et direkte API-kald springe godkendelsen over, genåbne en afsluttet sag eller starte den samme levering to gange.
Udgivet 23. september 2026 · Ca. 13 minutters læsetid
Find den arbejdsgang, hvor en forkert status gør skade
Vælg en konkret proces, som medarbejdere eller kunder allerede bruger: tilbud, booking, ordre, sag, faktura eller dokumentgodkendelse. Kortlæg ikke hele virksomheden på én gang. Målet er at gøre én vigtig arbejdsgang entydig nok til, at kode, test og drift kan være enige om resultatet.
| Tegn på et uklart workflow | Mulig konsekvens | Det skal afklares |
|---|---|---|
| Status kan vælges frit i en menu | Godkendelse eller kontrol springes over | Tilladte handlinger fra hver status |
| Flere boolean-felter beskriver samme forløb | Kombinationer som betalt og annulleret opstår samtidig | Én entydig status og særskilte fakta |
| Knappen skjules kun i brugerfladen | Et direkte request omgår reglen | Server- eller dataregel for overgangen |
| Status og email gemmes i to trin | Data ændres uden at beskeden sendes – eller omvendt | Atomisk registrering og sikker efterbehandling |
Skeln mellem status, handling og historik
De tre ting løser forskellige spørgsmål. Når de blandes sammen, bliver både skærme, rettigheder og fejlfinding uklare.
- Status
- Den aktuelle forretningstilstand, fx kladde, klar til kontrol, godkendt eller annulleret.
- Handling
- Brugerens hensigt, fx send til kontrol, godkend, afvis eller annullér. Handlingen vurderes mod den aktuelle status.
- Historik
- Hvem forsøgte eller gennemførte handlingen, hvornår, fra hvilken status, til hvilken status og med hvilket resultat.
En afvist handling skal normalt ikke ændre status. Den kan stadig være vigtig i en sikkerheds- eller driftslog, hvis forsøget viser fejl, misbrug eller en uklar arbejdsgang.
Skriv overgangene som en beslutningstabel
En simpel tabel er ofte mere nyttig end et stort diagram. Den gør skjulte antagelser synlige og kan senere omsættes til serverkode, databasekontroller og negative tests.
| Fra | Handling | Til | Aktør og betingelse | Efterfølgende arbejde |
|---|---|---|---|---|
| Kladde | Send til kontrol | Klar til kontrol | Ejer; påkrævede felter er udfyldt | Opret opgave til kontrollør |
| Klar til kontrol | Godkend | Godkendt | Kontrollør; seneste version er stadig den viste | Registrér hændelse og kø eventuel levering |
| Klar til kontrol | Send retur | Kladde | Kontrollør; begrundelse er angivet | Giv ejeren besked |
| Godkendt | Annullér | Annulleret | Særskilt rettighed; konsekvens er bekræftet | Stop ikke-udført arbejde og afstem udført arbejde |
Tabellen er et eksempel, ikke en standardskabelon. En lille intern app kan have få tilstande, mens betaling, myndighedsdata eller andre følsomme processer kan kræve genbekræftelse, flere roller eller en eksplicit godkendelse tæt på udførelsen.
Håndhæv overgangen dér, hvor data bliver skrevet
Brugerfladen må gerne skjule ugyldige knapper og forklare næste mulige handling. Den er stadig ikke sikkerhedsgrænsen. Et request kan ændres eller sendes uden om skærmen. Kontrollen skal derfor ligge i backend, databasefunktion, Security Rules eller en kombination, der passer til appens arkitektur.
- Læs serververificeret identitet og tilhørsforhold. Tag ikke rolle, virksomhed eller godkendt-status fra browserens felter.
- Læs den aktuelle tilstand. Beslutningen skal bruge den værdi, som gælder ved skrivningen, ikke en gammel skærmkopi.
- Kontrollér overgang, rettighed og forretningsbetingelser samlet. Afvis som standard, hvis kombinationen ikke er beskrevet.
- Gem status og nødvendig historik atomisk. Delvise opdateringer må ikke efterlade posten i en tilstand, som ingen kan forklare.
- Returnér et tydeligt udfald. Skeln mellem manglende adgang, ugyldig overgang og konflikt med en nyere ændring uden at lække data.
SQL: kombiner begrænsede værdier med en betinget opdatering
I PostgreSQL kan en CHECK-constraint begrænse, hvilke statusværdier en række må indeholde. Den beskriver ikke alene, om et bestemt skift fra gammel til ny status er tilladt. Det kan håndhæves i den transaktionelle serverkode, en databasefunktion eller et velafgrænset triggerdesign.
UPDATE orders
SET status = 'approved',
approved_by = $1,
approved_at = CURRENT_TIMESTAMP
WHERE id = $2
AND tenant_id = $3
AND status = 'ready_for_review'
RETURNING id, status;
-- 0 rows betyder: posten findes ikke, tilhører en anden virksomhed,
-- eller status er ændret siden brugerens skærm blev indlæst.Kontroller altid, om opdateringen faktisk returnerede en række. Hvis ikke, må appen læse den aktuelle situation og vise et sikkert svar i stedet for at antage, at godkendelsen lykkedes. Flere sammenhængende databaseændringer skal ligge i samme transaktion, når de kun giver mening samlet.
Firestore: sammenlign gammel og ny tilstand i reglerne
Cloud Firestore Security Rules kan sammenligne den eksisterenderesource.datamed den kommenderequest.resource.data. Med diff().affectedKeys()kan reglerne også afvise, at klienten ændrer andre felter samtidig.
match /databases/{database}/documents {
match /orders/{orderId} {
allow update: if request.auth != null
&& get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "reviewer"
&& get(/databases/$(database)/documents/users/$(request.auth.uid)).data.tenantId == resource.data.tenantId
&& resource.data.status == 'ready_for_review'
&& request.resource.data.status == 'approved'
&& request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['status', 'approvedBy', 'approvedAt'])
&& request.resource.data.approvedBy == request.auth.uid
&& request.resource.data.approvedAt == request.time;
}
}Hvis flere dokumenter skal ændres samlet, kan en transaktion eller batch kombineres med regler, der ser den kommende samlede tilstand viagetAfter(). Serverbiblioteker med administrativ adgang håndhæves ikke af klientreglerne; den samme workflowkontrol skal derfor være eksplicit i serverkoden og dens IAM-adgang. Eksemplets bruger- og rolledokument skal selv være beskyttet mod klientændringer.
Adskil statusskiftet fra email, betaling og integrationer
En godkendelse kan både ændre data og starte arbejde i et andet system. To uafhængige writes kan ikke behandles som én almindelig databaseopdatering: processen kan stoppe efter den ene, og netværk eller kø kan levere den anden mere end én gang.
- Gem hensigten sammen med status. Registrér et job eller en outbox-hændelse i samme atomiske skrivning, når teknologien understøtter det.
- Udfør sideeffekten bagefter. En worker kan sende mail, oprette en faktura eller kalde integrationen uden at holde brugerens request åbent.
- Gør behandlingen idempotent. Brug et stabilt hændelses-id, så en retry ikke skaber en ekstra levering.
- Vis særskilt driftsstatus. “Godkendt” og “sendt til økonomisystem” er to forskellige fakta, hvis det eksterne kald kan være forsinket eller fejle.
Kør ikke email, betaling eller andre eksterne sideeffekter inde i en Firestore- transaktionsfunktion. Firebase dokumenterer, at funktionen kan blive kørt igen ved samtidige ændringer. Gem i stedet et entydigt arbejde, som kan behandles sikkert efter commit.
Test de veje, som brugerfladen ikke viser
Et workflow er ikke bevist af en glad kliktest. Kald den samme backend eller dataregel med kontrollerede testbrugere og forsøg både de tilladte og de forbudte overgange.
- Spring direkte fra kladde til godkendt uden kontroltrinnet.
- Godkend som en bruger med forkert rolle eller fra en anden virksomhed.
- Send samme handling to gange med samme hændelses- eller idempotens-id.
- Godkend fra en gammel browsertab, efter en anden bruger har ændret posten.
- Ændr status og et beskyttet felt i samme request.
- Afbryd efter status er gemt, men før worker eller integration er færdig.
- Forsøg at genåbne eller annullere en sluttilstand uden den beskrevne undtagelsesvej.
- Bekræft at afslag ikke ændrer data eller starter sideeffekter.
Typiske fejl
- Status er et frit tekstfelt: Små staveforskelle bliver til nye tilstande, som kode og rapporter ikke kender.
- Rolle er lig med overgang: En administrator kan gøre alt fra alle tilstande uden at opfylde workflowets øvrige betingelser.
- Godkendelsesdata kommer fra klienten: Bruger-id, tidspunkt eller godkendt beløb kan ændres i requestet.
- Historikken overskrives: Kun den aktuelle status bevares, så fejl og tvister ikke kan rekonstrueres.
- Status betyder for mange ting: “Færdig” bruges både om intern godkendelse, afsendelse og ekstern modtagelse.
- Retry gentager sideeffekten: Samme overgang sender flere mails, opretter flere fakturaer eller leverer flere gange.
- Kun gyldige klik testes: Direkte requests, gamle skærme, konflikter og spring i rækkefølgen opdages først i drift.
Tjekliste før workflowet bruges i drift
- ☐ Én kritisk arbejdsgang har navngivne tilstande og klare sluttilstande.
- ☐ Hver handling beskriver fra-status, til-status, aktør, betingelser og resultat.
- ☐ UI viser relevante handlinger, men server eller dataregler håndhæver dem.
- ☐ Rolle og virksomhed bestemmes fra en serververificeret identitet.
- ☐ Statusværdier er begrænsede, og ugyldige kombinationer af felter bliver afvist.
- ☐ Overgangen bruger den aktuelle datatilstand og håndterer konflikter eksplicit.
- ☐ Status, aktør, tidspunkt og nødvendig hændelse gemmes atomisk.
- ☐ Email, betaling og integrationer behandles efter commit og kan genkøres sikkert.
- ☐ Revisionssporet kan forklare gennemførte og relevante afviste handlinger.
- ☐ Negative tests dækker spring, forkert rolle, forkert virksomhed og beskyttede felter.
- ☐ Samtidige handlinger, gamle browsertabs og gentagne requests er afprøvet.
- ☐ En driftsansvarlig kan se fastlåste eller fejlede sideeffekter og følge dem op.
Relaterede guides
Officielle kilder
Principper og eksempler er kontrolleret mod aktuelle officielle kilder. Den konkrete håndhævelse afhænger af appens datalager, identitetsmodel, integrationer og driftskonsekvens; kontrollér altid dokumentationen for den stack, appen faktisk bruger.
- OWASP: Business Logic Security Cheat Sheet
- OWASP: Transaction Authorization og tilladte statusskift
- PostgreSQL: Constraints og dataintegritet
- PostgreSQL: UPDATE med betingelser og RETURNING
- PostgreSQL: Transaktioner som samlet alt-eller-intet-handling
- Firebase: Begræns felter og sammenlign data før og efter en ændring
- Firebase: Transaktioner og atomiske batch-writes
- Firebase: Test Security Rules med Emulator Suite
- AWS Prescriptive Guidance: Transactional outbox
Virker appen, men kan status stadig springe uden om reglerne?
Startklar kan hjælpe med at kortlægge den konkrete arbejdsgang, placere reglerne ved den rigtige dataadgang og afprøve godkendelser, samtidighed, historik og sideeffekter i den stack, appen allerede bruger.
Se Startklar-forløbet