En översikt över hur DDD-aggregat, CQRS-commands, Event Sourcing via SqlStreamStore på MSSQL och en Blazor WebAssembly-klient hänger ihop till ett komplett system i .NET 10.
Lektion 1 — V1 mån. Vi sätter den mentala kartan: vad bygger vi, varför, och i vilken ordning kommer pusselbitarna in under kursen?
Varför just denna kombination?
Domain-Driven Design (DDD), Command Query Responsibility Segregation (CQRS) och Event Sourcing (ES) är tre olika mönster som ofta omnämns tillsammans, men de är fristående. Du kan göra DDD utan CQRS, CQRS utan ES, och ES utan DDD. När de kombineras uppstår dock en arkitektur där varje del förstärker de andra:
DDD ger oss aggregat som äger sina invarianter. Aggregatet är transaktionsgränsen.
CQRS separerar skriv-sidan (commands som ändrar aggregat) från läs-sidan (queries mot optimerade vyer). Det gör att aggregaten slipper bära ansvaret för rapportering.
Event Sourcing lagrar aggregatens historik som en serie händelser i stället för aktuellt tillstånd. Det ger oss en perfekt revisionslogg, möjligheten att bygga om read models från noll, och en naturlig modell för att "ångra" via kompenserande händelser.
Systemet vi bygger
Genom kursen växer en lösning fram med fem projekt:
Skriv och läs går olika vägar. Skrivningen rör aggregatet och appendar händelser. Läsningen träffar aldrig aggregatet — den läser ur en read-modell.
Aggregatet är inte CRUD. Klienten skickar inte "uppdatera saldo till 1500", utan en avsikt: "sätt in 500". Aggregatet validerar att det är tillåtet och bestämmer vilka händelser som uppstår.
Event store och read model ligger i samma MSSQL. Det här är ett medvetet val i kursen — det löser dual-write-problemet (vi återkommer till det i lektion 7).
Varför Event Sourcing i stället för CRUD?
Egenskap
CRUD (state-based)
Event Sourcing
Lagrar
Nuvarande tillstånd
Alla händelser som ledde dit
Revisionslogg
Behövs separat
Inbyggd — händelserna är loggen
Tidsresor
Svårt eller omöjligt
Replay till valfri tidpunkt
Nya rapporter
Migrera databasen
Bygg en ny projektion från event 0
"Ångra"
UPDATE tillbaka — historiken försvinner
Kompenserande händelse — historiken bevaras
Komplexitet
Låg
Hög — kräver eventversionering, projektioner, eventual consistency
När du inte ska använda Event Sourcing
Enkla CRUD-domäner (produktkatalog, inställningssidor, "edit a row"-UI) blir bara dyrare med ES. Använd ES när domänen är intressant — när det vad som hände är lika viktigt som det nuvarande värdet (finans, bokningar, regler-tunga arbetsflöden, audit-krav).
Teknikstack i kursen
Lager
Bibliotek / produkt
Roll
Runtime
.NET 10
LTS-grund
Klient
Blazor WebAssembly
SPA, körs i browsern, anropar API via HttpClient
API
ASP.NET Core Minimal API
HTTP-endpoints för commands och queries
Mediator
MediatR
Command/query-bus + pipeline behaviors
Event store
SqlStreamStore (MsSqlStreamStoreV3)
Append-only event-strömmar i MSSQL
Read model
Microsoft SQL Server + Dapper
Projektionstabeller, optimerade för läsning
Bakgrundsarbete
BackgroundService + Channels
Projektioner, command-kö, sagas
Validering
FluentValidation
Command-validering i pipeline
Resiliens
Polly
Retry vid concurrency-konflikt
Tester
xUnit + Testcontainers
Enhets- och integrationstester
Varför endast en MSSQL-instans?
Många ES-tutorials kör event store och read model i två olika databaser (t.ex. EventStoreDB + Postgres). Det fungerar bra i stora system, men introducerar dual-write-problemet: två separata transaktioner som kan misslyckas oberoende. Genom att lägga både event-strömmen (SqlStreamStore) och read-modellerna i samma MSSQL kan vi använda en enda transaktion när vi projicerar — vilket dramatiskt förenklar driften.
Kursens röda tråd
L1 — Helhet (denna sida). Vi sätter kartan.
L2 — DDD-grunder. Bounded context, entity, value object, domänhändelse.
L3 — AggregateRoot. Mönstret i C#, invarianter, basklass.
Lös övningarna självständigt. Det finns inget facit — lärandet sker i processen.
Rita om diagrammet Rita systemets dataflöde för en läsoperation (GET /accounts/{id}) i stället för en skrivning. Vilka komponenter berörs? Vilka berörs inte?
Identifiera CRUD vs ES Lista tre system du arbetat med (eller känner till). Vilka skulle vinna på Event Sourcing? Vilka skulle bli onödigt komplicerade? Motivera kort.
Hitta avsikten Många UI:n är "edit a form". Ta ett sådant UI du känner till — formulera om det till en lista av tydliga commands med avsikt. Hur många commands blev det?
Eventnamngivning Skriv tio events från ett system du känner till. Alla ska vara i dåtid och beskriva vad som hände, inte tekniska detaljer. Undvik ord som "Updated" och "Changed" — var specifik.
Soloprojektor
Projekt 1 — Arkitekturskiss
Välj en domän du känner väl (gärna från ditt nuvarande jobb). Producera ett arkitekturdokument (max 3 sidor) som beskriver: bounded contexts, aggregat, viktiga commands och events, och vilka read models som behövs. Rita ett komponentdiagram likt det i denna sida. Inkludera en kort motivering: varför ES här, vad blir komplicerat?
Projekt 2 — Bygg ett tomt skelett (fördjupning)
Sätt upp en .NET 10-lösning med de fem projekten (Domain, Application, Api, Projections, Web) plus tre testprojekt. Lägg in NuGet-referenser till MediatR, FluentValidation, Polly, SqlStreamStore, Dapper och xUnit. Lösningen ska kompilera utan kod. Sätt upp en docker-compose.yml som startar MSSQL 2022 lokalt.