Les 2 - ADR en ORM (EF core)
In deze les twee drieletterige afkortingen ADR en ORM, die niet per se heel veel met elkaar te maken hebben, maar wel allebei over documentatie/single source of truth gaan, en beide in je repository terecht komen (ORM Code first als code die het data model vastlegt, ADR als markdown bestanden die bij elkaar je software architectuur beschrijven). Beide horen wel bij DevOps en dragen dan ook bij CDMM Checkpoints; zie hiervoor de week opdracht.
"CDMM Beginner level Design & Architecture: At this level the importance of applying version control to database changes will also reveal itself." - https://www.infoq.com/articles/Continuous-Delivery-Maturity-Model/
1. ORM - Object Relation Mapper
In de workshop zijn kort 'volumes' behandeld in Docker, die nodig zijn voor persistent opslag. Althans als je data langer moet bestaan dat de container, wat op productie het geval is, als je een database gebruikt.
Neem de Docker documentatie `Volumes' (negeer de oude constructie 'mounts' e.d. dit is alleen voor oudere containers die dit gebruikten).
Lees het artikel DevOps for databases van Christian Melendez (z.d.). Als oefening kun je evt. de Contonso voorbeeld code downloaden, de dotnet ef tools installeren zoals het artikel aangeeft en de besproken oefening meedoen.
“Database refactorings […] involve three different changes that have to be done together
- Changing the database schema
- Migrating the data in the database
- Changing the database access code”
Quote 1: (Fowler, 2016)
Van de 3 wijzigingen uit quote 1 mist in simpele applicaties vaak het echte migreren van bestaande data. Dit vereist ook vaak het nadenken over hoe deze data te migreren en het handmatig schrijven/uitbreiden van de migratie code (de Up() en Down() methode in EF core.
1.1 Voorbeeld casus ORM: Data model van hypothetische CDMM applicatie
Het CDMM die wekelijks terugkomt, en je als student kan/moet gebruiken om je DevOps maturity te bepalen heeft ook een datamodel die wellicht nuttig is om expliciet te maken. In een les tekende ik dit grove data model (UML ERM diagram):
Figuur 1: ORM oefening. Het in een les besproken mogelijke klassendiagram voor de database in weekopdracht.
UML is prima om als 'schets' te gebruiken. Start on a whiteboard. Maar voor naslag is het wel nuttig een wat netter diagram te maken. Bijvoorbeeld met PlantuML of Mermaid zoals je al deed in weekopdracht 1: Documentation as code.
1.2 Upgraden naar diagrams as code
In bovenstaand diagram missen nog de categorie en het niveau van een 'CDMM checkpoint'. Oftewel hoe we het checkpoint in de tabel plaatsen qua kolom respectevelijk rij waar deze komt. In OO speak zijn dit CDMMCheckpointNiveau en CDMMCheckpointCategorie. Als we bovenstaande schets uitwerken tot een ERM diagram krijg je figuur 3. Dit ERD diagram geeft het gewenste datamodel van een CDMM Checkpoint.
[CDMMNiveau] Id Beschrijving Student 1--* CDMMSelfAssessment CDMMSelfAssessment 1--* CDMMEvaluation CDMMEvaluation 1--1 CDMMCheckpoint CDMMCheckpoint 1--* CDMMCategorie CDMMCheckpoint 1--* CDMMNiveau
[Student] *Naam +"Student nummer"
[CDMMSelfAssessment] Datum Student CDMMCheckpoint
[CDMMEvaluation] CDMMCheckpoint IsChecked Argumentatie
[CDMMCheckpoint] CDMMCategorie CDMMNiveau Code Titel Beschrijving
Figuur 2: ORM oefening. Het in de les besproken mogelijke klassendiagram voor de database in weekopdracht.
Op deze manier is het mogelijk voor een student om zelf een self assessment te doen door het CDMM in te vullen voor een bepaalde beroepsproduct en -project.
1.3 CDMM Applicatie -> User stories
Je zou zelfs een applicatie kunnen maken om CDMM beoordelingen te doen, waar dit nu nog een exercitie op papier is, of in markdown, om bij elke wekelijkse DevOps opdracht dit in te vullen, inclusief argumentatie voor het tonen van begrip cq. het verdiepen in CDMM.
Voor zo'n applicatie zijn hier enkele mogelijke user stories vanuit 'docent' en 'student' oogpunt.
- Als docent wil ik dat studenten een self assessment aan de hand van het Continuous Delivery Maturity Model (CDMM) kunnen maken en deze beargumenteren, zodat zij kunnen zien waar zij staan qua DevOps volwassenheid, en zich in DevOps best practices moeten verdiepen. - MUST
- Als student wil ik makkelijk een CDMM beoordeling kunnen invullen en hierbij achtergrond informatie krijgen over de verschillende CDMM checkpoints zodat ik informatie over bijbehorende punten kan krijgen, minder snel verkeerd interpreteer, en ook niet helemaal op de website te moeten opzoeken - SHOULD
- Als student wil ik makkelijk een ingevuld CDMM assessment kunnen exporteren, zodat ik deze — zowel als PDF en als markdown tekst — kan inleveren als bewijslast voor school en als markdown/documentation as code op kan nemen in mijn huiswerkopdrachten of beroepsproduct (DevOps BP) - COULD
- Als student wil ik iteratief een assessment invullen op verschillende momenten in een project en de ook zo kunnen terugzien, zodat ik vooruitgang zie en kan demonstreren in mijn project. - WANNAHAVE/WONTHAVE
Dit is een goede video om in de les samen te kijken ter relativering/van ORM's:
- Gorman, Christin (2011) (Vimeo). Zie bronnen.
Bekijk de Vimeo‑video (Gorman, 2011).

Ook 'Continuous Delivery' frontman Dave Farley haalt de video van Gorman aan in zijn boek Modern Software Engineering (tip!). Martin Fowler's artikel 'ORM hate' is een meer genuanceerde kijk op deze kwestie (Fowler, z.d.). Dat je geen SQL kennis zou hoeven te te hebben bij gebruik ORM is een drogreden, vanwege de ca. 10% gevallen die overblijft (en idd soms slechte performance). Hoewel de hoeveelheid SQL achtige annotaties in Gorman's wel wat hooggekozen is. Maar voor de resterende 10% moet je namelijk inzicht hebben in performance gevolgen. Voor SQL Server bv. van impact van bepaalde join volgorde en handmatige optimalisaties via keys toevoegen of verwijderen met hierachter technische concepten als 'table scan' vs 'index scan', etc. (Anvesh, 2023). En daarbij vergeleken is SQL syntax a walk in the park. Maar het 'single point of truth' of 'single point of definition' dat een goed ORM framework met ook 'migrations' mogelijkheden je kan geven. Al kom je als student niet snel in aanraking met een situatie waarbij je productie data hebt, die ook gemigreerd moet naast DB schema en applicatiecode, zoals Fowler in zijn artikel/blog 'Evolutionary database design' de big 3 aangeeft (Fowler, 2016).
1.4 Database First – overzicht en EF Core scaffolding
Bij Database First bestaat het gegevensmodel al als fysieke database: tabellen, kolommen, keys en relaties. Het database‑schema is de single source of truth. Vanuit deze bestaande database voer je code generatie uit (reverse engineering) om entiteitsklassen en een DbContext te maken. In EF Core doe je dit met scaffolding; migraties kun je daarna gebruiken voor vervolgwijzigingen, maar het initiële model komt uit de database, niet uit de code. Omdat het schema leidend is en al vastligt, speelt Convention over configuration hier minder een rol dan bij Code First.
Scaffold (voorbeeld SQL Server):
dotnet tool install --global dotnet-ef
dotnet ef dbcontext scaffold "Server=localhost,1433;Database=cdmm;User Id=sa;Password=Your_password123;TrustServerCertificate=True" \
  Microsoft.EntityFrameworkCore.SqlServer \
  --output-dir Models \
  --context CdmmContext \
  --force
1.5 Model First – conceptueel
Bij Model First begin je met een conceptueel domeinmodel. Je tekent met een visuele editor een ER diagram van entiteiten en relaties en behandelt dat ER‑model als single source of truth. Vanuit het model laat je code generatie uitvoeren (entiteiten, DbContext) en genereer je het databaseschema. In moderne .NET projecten vloeit Model First vaak samen met Code First: je werkt primair in code, maar hanteert dezelfde gedachte van één bron waaruit alles volgt. EF maakt hierbij veel gebruik van Convention over configuration, waardoor je “in de pit of success” (zie Coding Horror) valt met standaardconventies en alleen expliciet configureert als de conventies niet voldoen. Migration‑bestanden gebruik je om de doorvoering van modelwijzigingen gecontroleerd en versieerbaar te maken.
1.6 EF Core Code First – minimale voorbeeldsetup
Voorbeeld van een kleine Code First‐setup met DbContext, DbSet<> en mapping.
// Domeinmodel
public sealed class Checkpoint
{
    public int Id { get; set; }
    public string Code { get; set; } = string.Empty;  // bijv. "BD-203"
    public string Titel { get; set; } = string.Empty;
}
// DbContext met DbSets en eenvoudige configuratie
public sealed class CdmmContext : DbContext
{
    public DbSet<Checkpoint> Checkpoints => Set<Checkpoint>();
    public CdmmContext(DbContextOptions<CdmmContext> options) : base(options) { }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        var cp = modelBuilder.Entity<Checkpoint>();
        cp.ToTable("Checkpoint");
        cp.HasKey(x => x.Id);
        cp.Property(x => x.Code).HasMaxLength(16).IsRequired();
        cp.Property(x => x.Titel).HasMaxLength(200).IsRequired();
        cp.HasIndex(x => x.Code).IsUnique();
    }
}
Registratie in .NET (bijvoorbeeld in Program.cs):
builder.Services.AddDbContext<CdmmContext>(opt =>
    opt.UseSqlServer(builder.Configuration.GetConnectionString("CdmmDb")));
Connection string in appsettings.Development.json:
{
  "ConnectionStrings": {
    "CdmmDb": "Server=localhost,1433;Database=cdmm;User Id=sa;Password=Your_password123;TrustServerCertificate=True"
  }
}
1.7 EF Core migraties – Up() en Down()
Een EF migratie klasse (gegenereerd met dotnet ef migrations add Init), versimpeld om het idee te tonen:
public partial class Init : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Checkpoint",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Code = table.Column<string>(maxLength: 16, nullable: false),
                Titel = table.Column<string>(maxLength: 200, nullable: false)
            },
            constraints: table => { table.PrimaryKey("PK_Checkpoint", x => x.Id); });
        migrationBuilder.CreateIndex(
            name: "IX_Checkpoint_Code",
            table: "Checkpoint",
            column: "Code",
            unique: true);
    }
    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(name: "Checkpoint");
    }
}
Apply migraties locally/in CI:
dotnet ef database update
1.8 Flyway – voorbeeldbestand en run
Flyway werkt met versioned SQL‐scripts. Voorbeeld bestandsnamen en inhoud:
sql/
  V1__init_checkpoint.sql
  V2__add_unique_index_on_code.sql
V1__init_checkpoint.sql:
CREATE TABLE checkpoint (
  id INT PRIMARY KEY IDENTITY(1,1),
  code VARCHAR(16) NOT NULL,
  titel VARCHAR(200) NOT NULL
);
V2__add_unique_index_on_code.sql:
CREATE UNIQUE INDEX ux_checkpoint_code ON checkpoint(code);
Runnen (voorbeeld):
flyway -url="jdbc:sqlserver://localhost:1433;databaseName=cdmm" \
       -user=sa -password=Your_password123 \
       -locations=filesystem:sql migrate
1.7 Database First – overzicht en EF Core scaffolding
Bij Database First bestaat de database al (handmatig of via DBA gemaakt) en genereer je daaruit je ORM‐model en DbContext.
Stapjes in EF Core:
- Installeer de tooling lokaal (eenmalig):
dotnet tool install --global dotnet-ef
- Scaffold het model uit een bestaande database (voorbeeld SQL Server):
dotnet ef dbcontext scaffold "Server=localhost,1433;Database=cdmm;User Id=sa;Password=Your_password123;TrustServerCertificate=True" \
  Microsoft.EntityFrameworkCore.SqlServer \
  --output-dir Models \
  --context CdmmContext \
  --force
Daarna kun je verder met migraties (nieuwe wijzigingen) toevoegen zoals bij Code First. Het initiële model is echter uit de database afgeleid.
1.8 Model First – conceptueel (met examengerelateerde termen)
Bij Model First ontwerp je eerst het domeinmodel en leid je daaruit database en eventueel code af. Vaak gebruik je een
visuele editor om een ER diagram te tekenen. Vanuit dat model kan vervolgens code generatie plaatsvinden (entiteiten,
DbContext) en/of het genereren van SQL‐schema’s. Het model geldt dan als single source of truth: wijzigingen maak je in
het model en deze worden doorgezet naar database en code.
Bij Code First is de bron juist de code. Je definieert C# klassen en mapping, en legt wijzigingen vast via een migration
(EF Core migraties met Up()/Down()). EF hanteert hierbij vaak convention over configuration: met verstandige defaults
val je in de “pit of success” en hoef je minder expliciet te configureren; expliciete configuratie gebruik je alleen als de
conventies niet voldoen.
Samengevat in termen die je in antwoorden kunt gebruiken:
- Visuele editor: gebruikelijk bij Model First om het domein te modelleren.
- ER diagram: het diagram van entiteiten en relaties dat als startpunt dient bij Model First.
- Single source of truth: bij Model First is dit het model; bij Code First is dit de code.
- Code generatie: uit het model (Model First) of implicit door EF uit de code (Code First) naar DB‐schema.
- Migration: vooral gekoppeld aan Code First; incremental toepassen van schemawijzigingen.
- Convention over configuration: EF/ORM‐conventies verminderen configuratiewerk, met expliciete mapping waar nodig. Zie ook Convention over configurationop Wikipedia.
Architecture Decision Records
In moderne software development projecten wordt vaak te weinig gedocumenteerd. Nieuwe developers kunnen nergens overzicht krijgen over een project. En ook bestaande developers in een project vergeten soms waarom bepaalde keuzes zijn gemaakt en het blijft dan bij 'het is nou eenmaal'.
Een lichtgewicht manier om software architectuur te beschrijven zijn Architecture Decision records. Er bestaan ook tools om ADR records aan te maken, maar de meerwaarde hiervan is beperkt. De tool kan slechts een template neerzetten, de echte waarde (en tijdsbesteding) zit in het goed invullen, en ook per keer nadenken welke van de kopjes relevant is en hoe in te vullen. In plaats van als een robot templates invullen, die vervolgens niemand wil lezen, omdat het toch niet zoveel zegt.
"Software architecture is those decisions which are both important and hard to change. This means it includes things like the choice of programming language, something architects sometimes gloss over or dismiss. ... Said another way, software architecture is those decisions which, if made poorly, will make a project either succeed or fail, in a needlessly expensive way." - https://kylecordes.com/2015/fowler-software-architecture
In plaats van eenmalig de hele software architectuur op te stellen sluiten ADR's aan bij de Agile aanpak, met het idee van een ADR is ook om telkens als er een beslissing is genomen, of voorstel is gedaan een ADR aan te maken. Dit zo ergens op schrift (e.g. in een repo) vastleggen is ook een manier om telkens terugkomende discussies te voorkomen volgense deze Reddit post.
ADR's zijn dus ook 'text records' die direct bij de code zitten; in versiebeheer. Typisch in de vorm van markdown .md bestand. De naam van het bestand geeft kort de beslissing weer, of het gebied waar deze zich bevind. Verder geef je de ADR's vaak ook een datum mee, en ook een status, van 'Proposed' tot 'Accepted' (of 'Rejected') en later mogelijk zelfs nog 'Deprecated'. Bekijk wat voorbeelden in de PriemChecker repo.
"ADRs' greatest strength is their low barrier to entry. Since anyone on the team can write an ADR, everyone who wants can fill the role of software architect." — Michael Keeling (2022)
Hoe je deze keuzes vastlegt (documenteert) is weer een keuze op zich. Maar sowieso is het idee 'lichtgewicht' en direct bij de code in een tekst format, typisch markdown. Want de doelgroep is toch: mede developers/architects, kortom ICT'ers (en niet domein/business stakeholders, daar zijn andere documenten voor). Hierbij is het handig een template te gebruiken. Om de keuze stress op dit niveau wat te beperken, en zo te kunnen focussen op concretere en relevantere keuzes in techniek en domein modelleren beperken we in deze minor de opties hiervoor tot twee:
- 
Y-statements - Wellicht wel de beste optie is een heel korte en krachtige variant van ADR's genaamd y-statements(DocSoc, 2020). Dit is feitelijk een enkele zin, maar hierin zit ook al een stuk 'alternatives'.
- 
Nygard template - Of je gebruikt het template van Nygard, één van de grondleggers, volgens een artikel van Michael Keeling (2022). Omdat een 'decision' (=beslissing) ook opties impliceert (anders kies je nergens tussen) nemen we hierin ook alternatieven op. In het template van Nygard zit geen apart kopje, maar zouden alternatieven dan onder kopje 'Decisions' moeten. Of je plaatst ze al direct in de Context (zoals Nygard zelf deed in het artikel waarin hij ADR's introduceerde (Nygard, 2011)). Maar in het kader van het 'single responsibility' principe voeg je ten opzichte van Nygard's template een extra kopje toe: Alternatieven. In het kader van ['The pyramid principle'] (Minto, 2002) plaats je deze sectie ACHTER de 'Decision'.
Binnen DevOps mag je echter afwijken van rigide standaarden, als er een goede reden is, het gaat immers om "Individuals and interactions over processes and tools" (Agile Manifesto, Fowler et al., 2001). DevOps ligt immers in het verlengde van Agile (embrace change).
Dit is een heel stuk korter dan de volledige ADR in Nygard (Henderson, 2023) met kopjes:
- Status: What is the status, such as proposed, accepted, rejected, deprecated, superseded, etc.?
- Context: What is the issue that we're seeing that is motivating this decision or change?
- Decision: What is the change that we're proposing and/or doing?
- Consequences: What becomes easier or more difficult to do because of this change?
Zie voor een voorbeeld voor de PriemChecker casus de docs/ADR folder in de PriemChecker repo voorbeeld van standaard straks in het project.
ADR vorbeeld casus: Web applicatie server
In de weekopdracht van deze week moet je de vorige week gemaakte applicatie uitbreiden naar een WEB API en ook hosten met een webapplicatieserver. Een veelgebruikte is nginx (spreek uit: 'Engine X'). Hiervoor maak je ook kennis met ADR's: Architecture Decision Records.
"What are alternatives to nginx when you want a webapplication server for a .NET application that you also want to containerize?"
 
Leerdoelen
Check met onderstaande leerdoelen of je de behandelde stof begrepen hebt en toetsvragen kunt beantwoorden.
- Je kunt toelichten wat een ADR is, en waarom het documenteren van architectuurbeslissingen belangrijk is voor traceerbaarheid en langlopende projecten/lang draaiende software.
- Je kunt uitleggen wat een ORM is en hoe het het programmeren tegen relationele databases vereenvoudigt.
- Je kent het verschil tussen Code First, Model First en Database First aanpakken van ORMs als Entity Framework of Hibernate.
- Je kunt beschrijven wat de rol is van de Up() en Down() methodes bij Entity Framework
- Je begrijpt hoe migratietools zoals Entity Framework en Flyway bijdragen aan een DevOps-werkwijze door schemawijzigingen versieerbaar en reproduceerbaar te maken.
- Je kunt uitleggen wat Martin Fowler bedoelt met een evolutionary databases
Quiz
Test je kennis met deze korte multiple choice quiz.
Bronnen
- Anvesh, B. (2023, 1 augustus). Table scan vs index scan vs index seek. Medium. Geraadpleegd op 18 april 2024, van https://medium.com/silenttech/table-scan-vs-index-scan-vs-index-seek-f5cbb4e93478
- Fowler, M. (z.d.). ORM hate. MartinFowler.com. Geraadpleegd op 21 februari 2022, van https://martinfowler.com/bliki/OrmHate.html
- Fowler, M. (2016). Evolutionary database design. MartinFowler.com. Geraadpleegd op 21 februari 2022, van https://martinfowler.com/articles/evodb.html
- Gorman, C. (2011). Hibernate should be to programmers what cake mixes are to bakers: beneath their dignity. Vimeo. Geraadpleegd op 4 september 2025, van https://vimeo.com/28885655
- Melendez, C. (z.d.). DevOps for databases. Stackify. Geraadpleegd op 4 september 2025, van https://stackify.com/devops-for-databases/
- Nygard, M. (2011, 15 november). Documenting architecture decisions. Cognitect Blog. Geraadpleegd op 15 september 2025, van https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions
- Wikipedia. (z.d.). Convention over configuration. Geraadpleegd op 15 september 2025, van https://en.wikipedia.org/wiki/Convention_over_configuration
- Atwood, J. (2007). Falling Into The Pit of Success. Coding Horror. Geraadpleegd op 15 september 2025, van https://blog.codinghorror.com/falling-into-the-pit-of-success/
