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 workflowMulig konsekvensDet skal afklares
Status kan vælges frit i en menuGodkendelse eller kontrol springes overTilladte handlinger fra hver status
Flere boolean-felter beskriver samme forløbKombinationer som betalt og annulleret opstår samtidigÉn entydig status og særskilte fakta
Knappen skjules kun i brugerfladenEt direkte request omgår reglenServer- eller dataregel for overgangen
Status og email gemmes i to trinData ændres uden at beskeden sendes – eller omvendtAtomisk 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.

FraHandlingTilAktør og betingelseEfterfølgende arbejde
KladdeSend til kontrolKlar til kontrolEjer; påkrævede felter er udfyldtOpret opgave til kontrollør
Klar til kontrolGodkendGodkendtKontrollør; seneste version er stadig den visteRegistrér hændelse og kø eventuel levering
Klar til kontrolSend returKladdeKontrollør; begrundelse er angivetGiv ejeren besked
GodkendtAnnullérAnnulleretSærskilt rettighed; konsekvens er bekræftetStop 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.

  1. Læs serververificeret identitet og tilhørsforhold. Tag ikke rolle, virksomhed eller godkendt-status fra browserens felter.
  2. Læs den aktuelle tilstand. Beslutningen skal bruge den værdi, som gælder ved skrivningen, ikke en gammel skærmkopi.
  3. Kontrollér overgang, rettighed og forretningsbetingelser samlet. Afvis som standard, hvis kombinationen ikke er beskrevet.
  4. Gem status og nødvendig historik atomisk. Delvise opdateringer må ikke efterlade posten i en tilstand, som ingen kan forklare.
  5. 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.

PostgreSQL: godkend kun den forventede aktuelle status
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.

Firestore Rules: afgrænset godkendelsesovergang
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.

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