ADR-019: Konvensjonsbasert EF-kolonnemapping⚓︎
Kolonnenavn i
KaiDbContextutledes av en konvensjon — snake_case med tapsfri æøå-translitterering (æ→ae,ø→oe,å→aa) av CLR-egenskapsnavnet. HåndskrevneHasColumnNamebeholdes kun som bevisste unntak. RedundanteIsRequired(),HasConversion<int>()ogHasColumnType("text")er fjernet, og verdiobjekt-ID-konvertere registreres én gang iConfigureConventions.
Kontekst⚓︎
Entitetskonfigurasjonene hadde ~500 av 752 linjer som håndmappet det EF allerede kan utlede:
HasColumnName("saksnummer") for Saksnummer, IsRequired() på ikke-nullbare felt (NRT gir
dette gratis), HasConversion<int>() på enum-er (EF sitt standardvalg), og HasColumnType("text")
på strenger (Npgsql sitt standardvalg). Å legge til én kolonne kostet en linje i entiteten pluss
en mappinglinje. Verre: kolonner uten eksplisitt HasColumnName beholdt EF sitt PascalCase-navn,
så skjemaet var en blanding av snake_case og PascalCase (regelresultater."RunAt" ved siden av
sak_id).
Beslutning⚓︎
- Kolonnekonvensjon.
SnakeCaseColumnNamingConvention(enIModelFinalizingConventionregistrert iKaiDbContext.ConfigureConventions) setter hvert kolonnenavn til snake_case + translitterering av egenskapsnavnet. Den kjører påConfigurationSource.Convention, så et eksplisittHasColumnName(...)i en konfigurasjon vinner alltid. Konvensjonen gjelder kun Kais egne tabeller (Enova.Kai.*); tredjeparts skjemaer som TickerQ (ticker) beholder sine egne navn. - Tapsfri translitterering.
æ→ae,ø→oe,å→aa(ikke enkelttegns-foldø→o), så to ulike norske ord aldri kolliderer til samme kolonnenavn. - Redundans fjernet.
IsRequired()på ikke-nullbare felt,HasConversion<int>()på enum-er ogHasColumnType("text")på strenger er slettet — verifisert med en tom probe-migrasjon. - Verdiobjekt-ID-er.
SakId,RegelId,RegelresultatId(Guid) ogSaksnummerId(string) konverteres viaconfigurationBuilder.Properties<T>().HaveConversion<...>()iConfigureConventions, ikke per entitet. - Engangs-normalisering. Migrasjonen
SnakeCaseKolonnekonvensjongir de gjenværende PascalCase-kolonnene snake_case-navn (RENAME COLUMN, metadata-only i Postgres).
Bevisste unntak (beholder eksplisitt HasColumnName)⚓︎
| Kolonne(r) | Egenskap | Hvorfor |
|---|---|---|
sak_key, regel_id, sak_id |
CaseKey, RuleId, CaseId |
Engelsk egenskapsnavn, norsk kolonne — ikke utledbart. |
type, navn (på regler) |
Kind, Name |
Norsk kolonne for engelsk egenskap. |
soknad_sendt_dato |
Søknadsdato |
Speiler Mimir-gold-kolonnen (konvensjonen ville gitt soeknadsdato). |
statsstotteordning_*, soknadsrunde_id, tabellen virkemiddel_soknadsrunde |
Statsstøtteordning*, SøknadsrundeId |
Speiler Mimir-gold-navnene (ø→o). Se VirkemiddelMasterdataKildeAdapter. |
okonomisk_paavirkning |
ØkonomiskPåvirkning |
Speiler YAML-nøkkelen i regel-authoring (RegelDefinisjon). |
belop_*, soker_*, {klima,energi,effekt,innovasjon}_omsokt, prosjektperiode-kolonner |
Komplekse typer (Støttebeløp, Søker, Resultat, Prosjektperiode) |
Strukturelle prefiks-navn, delvis gold-speilende. |
applicable_ers_statuses beholder også eksplisitt navn fordi skygge-egenskapen heter
_applicableErsStatuses (konvensjonen ville tatt med understreken). SøknadsrundeId.IsRequired()
beholdes fordi NRT-required ikke utledes for skalar i en eid samling (OwnsMany).
Konsekvenser⚓︎
- En ny kolonne koster nå én linje i entiteten; ingen mappinglinje med mindre den er et unntak.
- Nye kolonner med æøå får deterministisk, tapsfritt navn uten at utvikleren tenker på det.
- Unntakene er få og har hver sin dokumenterte grunn (ekstern kontrakt eller struktur), så «hvorfor er akkurat denne håndmappet» er alltid svarbart.
- Regresjonsvern: en tom probe-migrasjon (
dotnet ef migrations add Probe) skal alltid være tom. Er den ikke det, har noen brutt konvensjonen eller lagt til et udokumentert avvik.