Lär dig en praktisk metod för att omvandla användarberättelser, entiteter och arbetsflöden till ett tydligt databasschema — och hur AI‑resonemang kan hjälpa dig hitta luckor och regler.

Ett databasschema är planen för hur din app kommer ihåg saker. I praktiska termer är det:
När schemat speglar verkligt arbete återspeglar det vad människor faktiskt gör—skapar, granskar, godkänner, planerar, tilldelar, avbokar—istället för vad som låter prydligt på en whiteboard.
Användarberättelser och acceptanskriterier beskriver verkliga behov med enkelt språk: vem gör vad och vad som räknas som “klart”. Om du använder dem som källa är det mindre sannolikt att schemat missar viktiga detaljer (t.ex. “vi måste spåra vem som godkände återbetalningen” eller “en bokning kan ombokas flera gånger”).
Att börja från berättelser håller dig också ärlig om omfattningen. Om det inte finns i berättelserna (eller arbetsflödet), behandla det som valfritt istället för att tyst bygga en komplicerad modell “ifall”.
AI kan hjälpa dig gå snabbare genom att:
AI kan inte pålitligt:
Behandla AI som en stark assistent, inte beslutsfattaren.
Om du vill omvandla den assistenten till momentum kan en vibe‑coding‑plattform som Koder.ai hjälpa dig gå från schema‑beslut till en fungerande React + Go + PostgreSQL‑app snabbare—samtidigt som du behåller kontroll över modellen, begränsningarna och migrationerna.
Schema‑design är en loop: draft → testa mot berättelser → hitta saknad data → förfina. Målet är inte ett perfekt första utkast; det är en modell du kan härleda tillbaka till varje användarberättelse och med säkerhet säga: “Ja, vi kan lagra allt detta arbetsflöde behöver—och vi kan förklara varför varje tabell finns.”
Innan du förvandlar krav till tabeller, var tydlig med vad du modellerar. Ett bra schema börjar sällan från ett blankt papper—det börjar från konkreta uppgifter människor utför och den bevisning du behöver senare (skärmar, utskrifter och kantfall).
User stories är rubriken, men de räcker inte ensamma. Samla:
Om du använder AI håller dessa inputs modellen jordad. AI kan föreslå entiteter och fält snabbt, men behöver verkliga artefakter för att undvika att uppfinna struktur som inte stämmer med produkten.
Acceptanskriterier innehåller ofta de viktigaste databasreglerna, även när de inte nämner data uttryckligen. Leta efter formuleringar som:
Vaga berättelser (“Som en användare kan jag hantera projekt”) döljer ofta flera entiteter och arbetsflöden. Ett annat vanligt gap är saknade kantfall som avbokningar, omförsök, delåterbetalningar eller omfördelning.
Innan du tänker på tabeller eller diagram, läs användarberättelserna och markera substantiven. I kravskrivning pekar substantiv ofta på de “saker” systemet måste komma ihåg—de blir ofta entiteter i ditt schema.
En enkel mental modell: substantiv blir entiteter, medan verb blir åtgärder eller arbetsflöden. Om en berättelse säger “En chef tilldelar en tekniker till ett jobb”, är troliga entiteter chef, tekniker och jobb—och “tilldelar” antyder en relation du modellerar senare.
Inte varje substantiv förtjänar sin egen tabell. Ett substantiv är en stark kandidat för en entitet när det:
Om ett substantiv bara dyker upp en gång eller bara beskriver något annat (“röd knapp”, “fredag”), kanske det inte är en entitet.
Ett vanligt misstag är att göra varje detalj till en tabell. Använd denna tumregel:
Två klassiska exempel:
AI kan snabba upp entity discovery genom att skanna berättelser och returnera en utkastlista med kandidat‑substantiv grupperade efter tema (personer, arbetsobjekt, dokument, platser). En användbar prompt är: “Extrahera substantiv som representerar data vi måste lagra, och gruppera dubbletter/synonymer.”
Behandla resultatet som en startpunkt, inte svaret. Ställ följdfrågor som:
Målet med Steg 1 är en kort, ren lista över entiteter du kan försvara genom att peka tillbaka på verkliga berättelser.
När du namngivit entiteterna (som Order, Customer, Ticket) är nästa jobb att fånga de detaljer du behöver senare. I en databas är de detaljerna fält (även kallade attribut)—påminnelserna systemet inte får glömma.
Börja med användarberättelsen och läs acceptanskriterierna som en checklista över vad som måste sparas.
Om ett krav säger “Användare kan filtrera beställningar efter leveransdatum”, så är delivery_date inte valfritt—det måste finnas som ett fält (eller kunna härledas pålitligt av annan sparad data). Om det står “Visa vem som godkände begäran och när”, behöver du troligen approved_by och approved_at.
Ett praktiskt test: Behöver någon detta för att visa, söka, sortera, granska eller räkna ut något? Om ja, hör det sannolikt hemma som ett fält.
Många berättelser innehåller ord som “status”, “typ” eller “prioritet.” Behandla dessa som kontrollerade vokabulärer—ett begränsat uppsättning tillåtna värden.
Om uppsättningen är liten och stabil kan ett enkelt enum‑fält fungera. Om den kan växa, behöver etiketter eller behörigheter (t.ex. admin‑styrda kategorier), använd en separat uppslags‑tabell (t.ex. status_codes) och lagra en referens.
Så här blir berättelser till fält du kan lita på—sökbara, rapporterbara och svåra att mata in felaktigt.
När du listat entiteterna (User, Order, Invoice, Comment, osv.) och skissat deras fält är nästa steg att koppla ihop dem. Relationer är lagret “hur dessa saker interagerar” som berättelserna antyder.
En‑till‑en (1:1) betyder “en sak har exakt en annan sak.”
User ↔ Profile (ofta kan dessa slås ihop om det inte finns skäl att separera).En‑till‑många (1:N) betyder “en sak kan ha många av en annan sak.” Detta är vanligast.
User → Order (lagra user_id på Order).Många‑till‑många (M:N) betyder “många saker kan relatera till många saker.” Detta kräver en extra tabell.
Databaser kan inte lagra “en lista med produkt‑ID:n” snyggt inne i Order utan problem senare (sökning, uppdatering, rapportering). Skapa istället en join‑tabell som representerar relationen.
Exempel:
OrderProductOrderItem (join‑tabell)OrderItem brukar innehålla:
order_idproduct_idquantity, unit_price, discountNotera hur berättelsens detaljer (“quantity”) ofta hör hemma på relationen, inte på någon av entiteterna.
Berättelser säger också om en koppling är nödvändig eller ibland saknas.
Order behöver en user_id (får inte vara tom).phone kan vara tomt.shipping_address_id kan vara tom för digitala varor.En snabb kontroll: om berättelsen antyder att du inte kan skapa posten utan länken, behandla den som obligatorisk. Om berättelsen säger “kan”, “får” eller ger undantag, behandla den som valfri.
När du läser en berättelse, skriv om den som en enkel parning:
User 1:N CommentComment N:1 UserGör detta för varje interaktion i dina berättelser. I slutändan har du en sammanhängande modell som matchar hur arbetet faktiskt sker—innan du öppnar ett ER‑diagramverktyg.
Användarberättelser berättar vad folk vill. Arbetsflöden visar hur jobbet faktiskt rör sig, steg för steg. Att översätta ett arbetsflöde till data är ett av de snabbaste sätten att fånga “vi glömde att spara det”-problem—innan du bygger något.
Skriv arbetsflödet som en sekvens av åtgärder och tillståndsbyten. Exempel:
De markerade orden blir ofta ett status‑fält (eller en liten “state”‑tabell) med tydliga tillåtna värden.
När du går igenom varje steg, fråga: “Vad måste vi kunna veta senare?” Arbetsflöden avslöjar ofta fält som:
submitted_at, approved_at, completed_atcreated_by, assigned_to, approved_byrejection_reason, approval_notesequence för flerstegsprocesserOm ditt arbetsflöde inkluderar väntan, eskalering eller överlämningar behöver du vanligtvis åtminstone en tidsstämpel och ett “vem har det nu”‑fält.
Vissa arbetsflödessteg är inte bara fält—de är separata datastrukturer:
Ge AI både: (1) användarberättelserna och acceptanskriterierna, och (2) arbetsflödets steg. Be den lista varje steg och identifiera vilken data som krävs för varje (status, aktör, tidsstämplar, outputs), och markera krav som inte kan stödjas av nuvarande fält/tabeller.
I plattformar som Koder.ai blir denna “gap check” särskilt praktisk eftersom du snabbt kan iterera: justera schemaantaganden, regenerera scaffolding och fortsätta utan långa manuella omvägar.
När du förvandlar användarberättelser till tabeller bestämmer du också hur data förblir identifierbar och konsekvent över tid.
En primärnyckel identifierar unikt en post—tänk på den som radens permanenta ID‑kort.
Varför varje rad behöver en: berättelser antyder uppdateringar, referenser och historik. Om en berättelse säger “Support kan se en order och ge en återbetalning” behöver du ett stabilt sätt att peka på den ordern—även om kunden byter e‑post, adressen redigeras eller orderstatus ändras.
I praktiken är det ofta ett internt id (nummer eller UUID) som aldrig ändras.
En foreign key är hur en tabell säkert pekar på en annan. Om orders.customer_id refererar customers.id kan databasen försäkra att varje order tillhör en verklig kund.
Detta matchar berättelser som “Som en användare kan jag se mina fakturor.” Fakturan flyter inte fritt; den är kopplad till en kund (och ofta till en order eller prenumeration).
Användarberättelser innehåller ofta dolda unikhetskrav:
Dessa regler förhindrar förvirrande duplikat som annars dyker upp månader senare som “databugs”.
Index gör uppslagningar snabba, som “hitta kund via e‑post” eller “lista ordrar per kund”. Börja med index som speglar dina vanligaste frågor och unikhetsregler.
Vad som kan vänta: tung indexering för sällsynta rapporter eller spekulativa filter. Dokumentera dessa behov i berättelserna, validera schemat först och optimera sedan baserat på verklig användning och långsamma frågor.
Normalisering har ett enkelt mål: förhindra konflikterande dubbletter. Om samma fakta kan sparas på två ställen kommer den förr eller senare att avvika (två stavningar, två priser, två “aktuella” adresser). Ett normaliserat schema lagrar varje fakta en gång och refererar till den.
1) Se upp för upprepade grupper
Om du ser mönster som “Phone1, Phone2, Phone3” eller “ItemA, ItemB, ItemC” är det en signal för en separat tabell (t.ex. CustomerPhones, OrderItems). Upprepade grupper gör det svårt att söka, validera och skala.
2) Kopiera inte samma namn/detaljer i flera tabeller
Om CustomerName förekommer i Orders, Invoices och Shipments har du flera sanningskällor. Håll kunddetaljer i Customers och spara endast customer_id på andra ställen.
3) Undvik “flera kolumner för samma sak”
Kolumner som billing_address, shipping_address, home_address kan vara okej om de verkligen är olika begrepp. Men om du modellerar “många adresser av olika typer”, använd en Addresses‑tabell med ett type‑fält.
4) Separera lookup från fri text
Om användare väljer från en känd uppsättning (status, kategori, roll), modellera det konsekvent: antingen ett begränsat enum eller en uppslags‑tabell. Detta förhindrar “Pending” vs “pending” vs “PENDING”.
5) Kontrollera att varje icke‑ID‑fält beror på rätt sak
Ett hjälpsamt tumtest: i en tabell, om en kolumn beskriver något annat än tabellens huvudentitet, hör den sannolikt hemma någon annanstans. Exempel: Orders bör inte lagra product_price om det inte betyder “pris vid beställningstid” (en historisk snapshot).
Ibland lagrar du medvetet dubbletter:
Nyckeln är att det är avsiktligt: dokumentera vilket fält som är sanningens källa och hur kopior uppdateras.
AI kan flagga misstänkt duplication (upprepade kolumner, liknande fältnamn, inkonsekventa “status”fält) och föreslå uppdelningar till tabeller. Människor väljer fortfarande avvägningen—enkelhet vs flexibilitet vs prestanda—baserat på hur produkten faktiskt kommer användas.
En användbar regel: spara fakta du inte pålitligt kan återskapa senare; beräkna allt annat.
Sparad data är sanningskällan: individuella radposter, tidsstämplar, statusändringar, vem gjorde vad. Beräknad data produceras från dessa fakta: totalsummor, räknare, flaggor som “är försenad” och summeringar som “aktuellt lager”.
Om två värden kan beräknas från samma underliggande fakta, föredra att spara fakta och beräkna resten. Annars riskerar du motstridigheter.
Härledda värden ändras när deras input ändras. Om du sparar både input och resultat måste de hållas synkade i varje arbetsflöde och kantfall (ändringar, återbetalningar, partiella leveranser, bakdaterade ändringar). Ett missat uppdateringssteg och databasen börjar berätta två olika historier.
Exempel: spara order_total samtidigt som du sparar order_items. Om någon ändrar en kvantitet eller applicerar en rabatt och totalen inte uppdateras perfekt ser ekonomi ett tal medan kundvagnen visar ett annat.
Arbetsflöden visar när du behöver historisk sanning, inte bara “nuvarande sanning”. Om användare behöver veta vilket värde var vid tidpunkten, spara en snapshot.
För en order kan du spara:
order_total vid checkout (snapshot), eftersom skatt, rabatter och prissättningsregler kan ändras senareFör lager beräknas ofta “lagernivå” från rörelser (inköp, försäljning, justeringar). Men om du behöver revisionsspår sparar du rörelser och eventuellt periodiska snapshots för rapporteringshastighet.
För inloggningsspårning, spara last_login_at som en faktahändelse (tidsstämpel). “Är aktiv de senaste 30 dagarna?” förblir beräknat.
Låt oss använda en bekant supportticket‑app. Vi går från fem användarberättelser till en enkel ER‑modell (entiteter + fält + relationer), och sedan kontrollerar vi den mot ett arbetsflöde.
Från dessa substantiv får vi kärnentiteter:
Före (vanligt miss): Ticket har assignee_id, men vi glömde säkerställa att bara agenter kan vara assignees.
Efter: AI flaggar detta och du lägger till en praktisk regel: assignee måste vara en User med role = “agent” (implementeras via applikationsvalidering eller en databasconstraint/policy beroende på stack). Det förhindrar “tilldelad till kund”‑data som senare bryter rapporter.
Ett schema är bara “klart” när varje användarberättelse kan besvaras med data du faktiskt kan spara och fråga. Det enklaste valideringssteget är att plocka upp varje berättelse och fråga: “Kan vi svara på detta från databasen, pålitligt, i alla fall?” Om svaret är “kanske” har din modell en lucka.
Skriv om varje användarberättelse till ett eller flera testfrågor—saker du förväntar dig att en rapport, skärm eller API ska fråga. Exempel:
Om du inte kan uttrycka en berättelse som en tydlig fråga är berättelsen otydlig. Om du kan uttrycka den—men inte kan svara med ditt schema—saknas ett fält, en relation, en status/händelse eller en constraint.
Skapa en liten dataset (5–20 rader per nyckeltabell) som innehåller normala fall och besvärliga fall (duplikat, saknade värden, avbokningar). “Spela igenom” berättelserna med dessa data. Du ser snabbt problem som “vi kan inte avgöra vilken adress som användes vid köpet” eller “vi har ingenstans att spara vem som godkände ändringen”.
Be AI generera valideringsfrågor per berättelse (inklusive kantfall och raderingsscenarier) och lista vilken data som krävs för att svara på dem. Jämför listan med ditt schema: varje mismatch är en konkret åtgärdspunkt, inte en vag känsla av att “något är fel”.
AI kan snabba upp datamodellering, men ökar också risken för att känslig information läcker eller att dåliga antaganden hårdkodas. Behandla det som en mycket snabb assistent: användbar, men behöver styrning.
Dela inputs som är realistiska nog för att modellera, men sanerade för att vara säkra:
invoice_total: 129.50, status: "paid")Undvik allt som kan identifiera personer eller röja konfidentiell drift:
Om du behöver realism, generera syntetiska provdata som matchar format och intervall—kopiera aldrig produktionsrader.
Scheman misslyckas oftast eftersom “alla antog” olika saker. Bredvid ditt ER‑diagram (eller i samma repo) håll en kort beslutslogg:
Detta förvandlar AI‑output till teamkunnande istället för en engångsartifact.
Ditt schema kommer att utvecklas med nya berättelser. Skydda det genom att:
Om du använder en plattform som Koder.ai, dra nytta av guardrails som snapshots och rollback när du itererar på schemaändringar, och exportera källkoden när du behöver djupare anpassning eller traditionell granskning.
Börja med berättelserna och markera substantiv som representerar saker systemet måste komma ihåg (t.ex. Ticket, User, Category).
Främja ett substantiv till en entitet när det:
Håll en kort lista du kan motivera genom att peka på specifika meningar i berättelserna.
Använd ett “attribut vs entitet”-test:
customer.phone_number).En snabb ledtråd: om du någonsin behöver “många av dessa”, behöver du sannolikt en annan tabell.
Behandla acceptanskriterier som en lagringschecklista. Om ett krav säger att du måste filtrera/sortera/visa/granska något måste du lagra det (eller härleda det på ett pålitligt sätt).
Exempel:
approved_by, approved_atdelivery_dateSkriv om berättelsetexterna till relationsmeningar:
customer_id på orders)order_items)Om relationen själv har data (quantity, price, role) hör den datan till join‑tabellen.
Modellera M:N med en join‑tabell som innehåller båda främmande nycklarna plus relationsspecifika fält.
Typiskt mönster:
ordersproductsGå igenom arbetsflödet steg för steg och fråga: “Vad behöver vi bevisa att detta hände senare?”
Vanliga tillägg:
submitted_at, closed_atBörja med:
id)orders.customer_id → customers.id)Lägg sedan till index för de vanligaste uppslagningarna (t.ex. , , ). Skjut upp spekulativ indexering tills du ser verkliga sök‑/prestandamönster.
Gör en snabb konsistenskontroll:
Phone1/Phone2, dela upp i en barn‑tabell.Denormalisera bara senare med ett klart syfte (prestanda, rapportering, audit) och dokumentera vad som är auktoritativt.
Lagra fakta du inte kan återskapa pålitligt senare; beräkna resten.
Bra att lagra:
Bra att beräkna:
Om du lagrar härledda värden (t.ex. ), bestäm hur det hålls synkat och testa kantfallen (återbetalningar, ändringar, partiella leveranser).
Använd AI för utkast och snabba förslag, men verifiera alltid mot era artefakter.
Praktiska promptar:
Skyddsåtgärder:
emailorder_items med order_id, product_id, quantity, unit_priceUndvik att lagra “en lista med ID:n” i en kolumn—det blir svårt att söka, uppdatera och upprätthålla integritet.
created_by, assigned_to, closed_byrejection_reasonOm du behöver veta “vem ändrade vad och när”, lägg till en event-/audit‑tabell istället för att skriva över ett fält.
emailcustomer_idstatus + created_atorder_total