Top Banner
Examensarbete Andreas Larsson 2012-06-05 Ämne: Datavetenskap Nivå: G1E Kurskod: 1DV40E Command Query Responsibility Segregation och Event Sourcing som mjukvaruarkitektur
33

Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

Jun 30, 2020

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

Examensarbete

Andreas Larsson

2012-06-05

Ämne: Datavetenskap

Nivå: G1E

Kurskod: 1DV40E

Command Query Responsibility

Segregation och Event Sourcing som

mjukvaruarkitektur

Page 2: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

Abstrakt

I den här rapporten undersöks Command Query Responsibility Segregation (CQRS),

Event Sourcing och Task Based User Interface (händelsebaserat gränssnitt) som

tillsammans bildar en annorlunda arkitektorisk uppbyggnad av system. Tanken bakom

arkitekturen är att kunna spåra vad, när och hur data förändras i ett system samt

möjlighet att återskapa tidigare tillstånd, jämfört med traditionella system som endast

lagrar, hanterar och presenterar data i dess nuvarande tillstånd. Undersökningen har

gjorts genom att ta fram en bokningsprototyp och resulterade i för –och nackdelar,

användningsområde och hur CQRS, Event Sourcing och händelsebaserat gränssnitt kan

implementeras tillsammans.

Abstract

In this report examines Command Query Responsibility Segregation (CQRS), Event

Sourcing and Task-Based User Interface, which together forms a different architecture

when building systems. The idea behind the architecture is to be able to track what,

when and how data changes in a system, and also the ability to recreate a previous states.

This can be compared to traditional systems which only stores, manages and presents

data in its current state. The poll was conducted by producing a booking prototype

system, resulting in the advantages and disadvantages of the architecture, when to use it

and how CQRS, Event Sourcing and Task Based User Interface can be implemented

together.

Page 3: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

Förord

Denna rapport skrevs som ett avslutande examensarbete inom datavetenskap för

programmet Webbprogrammerare på Linnéuniversitetet i Kalmar. Idén till arbetet kom

från företaget DotNet Mentor i Göteborg.

Jag vill passa på att tacka min handledare på universitetet, Mats Loock, för hjälp med

framställandet av denna rapport. Jag vill även tacka utvecklarna på DotNet Mentor,

framförallt Mikael Egnér och Kristoffer Ahl, för uppdraget och för all hjälp och goda

råd.

Page 4: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

Innehållsförteckning

Abstrakt ................................................................................................................ I

Abstract ................................................................................................................ I

Förord ..................................................................................................................II

1. Bakgrund ...................................................................................................... 1

1.1 Introduktion ..................................................................................................................... 1

1.2 Frågeställning ................................................................................................................... 4

1.3 Avgränsningar .................................................................................................................. 4

2. Metod ........................................................................................................... 6

2.1 Metoddiskussion .............................................................................................................. 6

3. Genomförande .............................................................................................. 7

4. Resultat ........................................................................................................ 8

4.1 Implementation av CQRS, Event Sourcing och Task Based UI ............................. 8

4.1.1 Domain Driven Design och Aggregate Roots .................................................. 8

4.1.2 Commands .............................................................................................................. 9

4.1.3 Command Handlers ............................................................................................. 11

4.1.4 Events .................................................................................................................... 12

4.1.5 Event Handlers ..................................................................................................... 12

4.1.6 Task Based UI....................................................................................................... 13

4.1.7 Sagas ....................................................................................................................... 14

4.2 Flöde CQRS och ASP.NET MVC ............................................................................. 16

5. Analys ......................................................................................................... 18

5.1 Fördelar ........................................................................................................................... 18

5.2 Nackdelar ........................................................................................................................ 20

6. Diskussion och slutsats .............................................................................. 21

7. Källförteckning ........................................................................................... 24

8. Bilagor ........................................................................................................ 26

8.1 Bilaga 1: <Exempel på aggregatrot med entiteter> ................................................. 26

Page 5: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

8.2 Bilaga 2: <Lagring av bokningsöversiktsdata i läsdatas>........................................ 27

Page 6: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

1

1. Bakgrund

1.1 Introduktion

Många system, webbsidor och applikationer erbjuder användare möjligheter att,

förutom att läsa information, även förändra information, alltså i grunden data. En

förändring av data innebär att en användare antingen lägger till, förändrar befintlig

eller tar bort data. Data lagras oftast i någon form av databas, och det traditionella

sättet för ett system att hantera förändringar av data är genom CRUD (Create, Read,

Update, Delete), alltså skapa, hämta, uppdatera och ta bort data. När en användare av

ett system på något sätt utför en handling som påverkar data, kommer förändringen

att genomföras i databasen och nästa gång samma data efterfrågas kommer det

förändrade tillståndet att presenteras.

I en del fall är det kanske det interaktionen handlar om, att förändra och hämta data,

men de flesta system är byggda för att hantera någon form av affärsverksamhet. För en

användare som ändrar sin adress innebär det att denne just ändrar sin adress, men för

en affärsverksamhet kanske det innebär; ”-Var adressen felaktig eller har personen

flyttat och vi ska skicka ut ett erbjudande?”.

Ur affärssynpunkt finns det alltså tillfällen då ägaren av ett system inte bara vill veta

nuvarande tillstånd på data utan även hur, när och varför det blivit så. För att ta det ett

steg längre, varför inte erbjuda ägaren av ett system att kunna spåra alla händelser som

sker och möjligheten att återskapa ett tidigare tillstånd.

Med ett traditionellt CRUD – system, som lagrar data i dess nuvarande tillstånd, blir

det svårt att erbjuda ovan nämnda tjänster. För att kunna möta det behovet måste

utvecklare tänka annorlunda. Data i en databas ser troligtvis ut som det gör för att

användare av ett system har genomfört en eller flera händelser i gränssnittet, exempelvis

klickat på en knapp, som i sin tur kommer att förändra data i databasen, exempelvis en

adress. Om det är händelser som styr tillståndet av data, varför inte lagra dessa istället?

Event Sourcing ser till att alla förändringar i ett system fångas som en sekvens av

händelser (Events), vilket innebär att händelseobjekt lagras i den ordning som de sker.

Page 7: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

2

Genom att köra igenom alla händelseobjekt kan nuvarande tillstånd på data skapas,

och förutom att systemet vet exakt vad som hänt, är det möjligt att återskapa ett

tidigare tillstånd av systemet [1].

Att lagra händelser öppnar upp möjligheter för att spåra exakt vad som hänt, när och i

vilken ordning, men varför en händelse har skett är fortfarande oklart. Som nämndes i

exemplet tidigare; om det är viktigt för en affärsverksamhet att skilja på om

användaren ändrar en felaktig adress och att ändra adress för att denne har flyttat

behöver systemet kunna hantera användarens intentioner, det vill säga kunna spåra vad

användaren har i åtanke när denne gör en förändring. I ett traditionellt CRUD -

baserat gränssnitt, det vill säga låta användaren klicka på en ”ändra-knapp” och sedan

ändra i ett eller ett par fält och klicka på spara, är det svårt att spåra användarens

egentliga avsikt. Genom att istället erbjuda ett så kallat uppgiftsbaserad gränssnitt, Task

Based User Interface, följer man användarens intentioner och låter denne utföra

kommandon som representerar olika avsikter [2]. Att ändra en felaktig adress och att

användaren har flyttat innebär i slutändan samma resultat, det vill säga en ändrad

adress, men genom att skicka kommandona ”CorrectMisspelledAddress” eller

”MoveCustomerToNewAddress” till systemet kan man spåra varför saker och ting

sker.

I ett system presenteras vanligtvis nuvarande tillståndet av data för användaren, och att

”spela upp” alla lagrade händelser enligt Event Sourcing för att visa nuvarande

tillståndet är inte speciellt effektivt. I ett CRUD – system används oftast, för lagring av

data, en relationsdatabas med flera tabeller som oftast har kopplingar sinsemellan. Att

exempelvis visa en kundfaktura innehållandes kunduppgifter, namn på köpta

produkter, pris och så vidare innebär att data sammanfogas från flera olika tabeller.

För att presentera data från olika tabeller används ofta någon form av ORM (Object

Relational Mapper) och modellklasser med egenskaper som representerar kolumner i

tabellerna i databasen. Ibland konverteras data till vymodellkasser och i en del fall sker

beräkningar innan data ska presenteras. Koden är oftast separerad i olika ”lager” så

som dataåtkomstlager, affärslogiklager och presentationslager. Alla dela fyller sin

funktion väl när data ska förändras, men varför gå igenom alla dessa steg för att

presentera data? Varför inte bara hämta data och visa den?

Page 8: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

3

Command, Query, Responibility, Segregation (CQRS) är ett designmönster där man delar

upp läsning från skrivning och låter de vara oberoende av varandra. Uppdelningen

beror på om en handling i ett system är ett kommando (command), det vill säga en

metod som kommer att förändra eller lägga till data, eller om handlingen är en fråga

(query), det vill säga en metod som returnerar data. Genom att dela upp och ta bort

beroendena mellan skriv och läs finns möjligheten att på ”lässidan” lagra data enligt

devisen en tabell per vy, det vill säga att data lagras på det sättet det ska presenteras [2]

[3].

När utvecklare pratar om CQRS - arkitektur är det vanligt att de menar CQRS

tillsammans med Event Sourcing och Task Based UI. Tillsammans bildar de ett flöde,

väldigt förenklat, enligt Figur 1.

Figur 1: Arkitektonisk översikt CQRS med Event Sourcing

Via ett händelsebaserat gränssnitt (Tasked Based UI) utför användaren av systemet ett

kommando (Command) som har för avsikt att förändra tillståndet på data.

Kommandot valideras, det vill säga ser till att data är i korrekt format, innan en eller

Page 9: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

4

flera metoder i domänmodellen anropas. Domänobjektets nuvarande tillstånd

återskapas genom att spela upp alla tidigare händelser (Events) kopplade till

domänobjektet. Domänmodellen har hand om affärslogik, till exempel kontrollera om

kunden har gjort fler än fem beställningar och i så fall erbjuda kunden rabatt.

Domänen utför det kommandot sa till den att göra och skapar händelser som

återspeglar vad som hänt och lagrar dessa i en händelsedatabas (Event Store).

Händelserna skickas också till en ”Event bus” för att tas omhand av händelsehanterare

(Event Handlers) som har i uppgift att uppdatera läsmodellerna i läsdatabasen (Read

Databases). När användaren av systemet frågar efter information hämtas data direkt

från läsdatabasen, där data lagras precis som det ska presenteras. Med andra ord sker

inga sammanfogningar och beräkningar tabeller emellan.

Termen CQRS (tillsammans med EventSourcing) dök upp ungefär runt 2009 och har

på senare år blivit lite av ett så kallat ”buzzword”, där en del förespråkar

designmönstret starkt medan vissa menar på att man skapar komplexitet där det inte

fanns förut. Avsikten med denna rapport är att undersöka hur det är att bygga ett

system enligt CQRS, vilka för- och nackdelar, vilka möjligheter det skapar, hur man

går tillväga för att spåra användarens intentioner och vad denne gör i systemet.

1.2 Frågeställning

Frågeställningar som kommer att undersökas närmare är:

Hur används och vilka för –och nackdelar jämfört med ett traditionellt system

innebär det att använda CQRS-arkitekturen?

Hur hanteras data?

På vilket sätt är det möjligt att spåra vad användaren gjort i systemet och ta

fram rapporter över det?

Är en implementation av CQRS komplex?

1.3 Avgränsningar

Då arbetet är tidsbegränsat kommer fokus att ligga på att undersöka grundläggande

delar i arkitekturen. Det är känt att CQRS inte är applicerbart för alla typer av system,

och undersökningen kommer att göras på ett system där det troligtvis skulle vara

Page 10: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

5

användbart. Hade undersökningen gjorts på ett system som inte alls passar med CQRS

resultatet troligtvis blivit annorlunda.

Page 11: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

6

2. Metod

För att få svar på frågeställningarna kommer en prototyp i form av ett bokningssystem

för färjor att utvecklas. Ett bokningssystem består av två delar, själva bokningsdelen

för kunder och ett administrationssystem. Då ett av kraven på administrationsdelen

kommer att vara att kunna spåra vad användare gör i systemet, måste gränssnittet för

kunderna vara ett Tasked Based UI som skickar olika kommandon beroende på vad

kunden gör. Bokningssystem är också bra för att se hur man i CQRS kan hantera hur

en händelse påverkar en annan del av systemet, t.ex. att genomföra bokningen

kommer, förutom att påverka bokningen, att påverka antalet bokade passagerare på

berörd avgång.

De praktiska erfarenheterna som prototypen förhoppningsvis ska inbringa kommer

att vävas ihop med andra personers slutsatser och förhoppningsvis ge svar på

frågeställningarna som rapporten tar upp. Det finns väldigt många diskussionsgrupper

på internet där det råder delade meningar om vad CQRS är, när det ska användas och

hur det ska implementeras, och rapporten kommer att sträva efter att följa vad Young

[2] och Dahan [4], som var de första att nämna CQRS - mönstret, anser.

2.1 Metoddiskussion

Enligt förespråkarna av CQRS är det inte till att användas för alla system, Abdullin

menar att det passar för mindre än 20% av fallen [4]. Att veta när det passar in och

inte är svårt att veta på förhand, ett bokningssystem kanske inte alls är rätt ställe att

använda det på, vilket kan ge en orättvis bild av resultatet om det visar sig att det inte

alls passar. Däremot innehåller ett bokningssystem ”samarbete” mellan flera

användare, det vill säga att fler än en person arbetar mot samma data (flera personer

kan göra bokningar på samma avresa samtidigt). Detta är enligt Dahan ett av

grundkraven för att använda CQRS [5].

Page 12: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

7

3. Genomförande

Då CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under

begränsad tid, föll valet på att använda ett ramverk för att underlätta utvecklingen av

bokningsprototypen. ”SimpleCQRS” 1, som är ett litet CQRS – ramverk för .NET, har

använts tillsammans med Jonathan Olivers ”EventStore” 2 för att underlätta arbetet

med CQRS – arkitekturen och lagring av händelser.

Ovanpå det har två ASP.NET MVC 4 3 - projekt skapats för webbgränssnittet, en för

bokningsdelen för kunder samt en för administrationsdelen. För att hantera

realtidsuppdateringar , alltså uppdatera data på användarnas skärmar då det sker

förändringar på servern, har biblioteket SignalR 4 använts.

För lagring av data föll valet på att använda en dokumentbaserad databas, framförallt

för att läsdatabasen ska lagra data på det sättet som det ska visas. RavenDB 5 är en

dokumentdatabas som lagrar data i formatet JSON, som är väl integrerat med .NET

vilket gjorde att det var smidigt att arbeta med. Lagring av händelser sker också i

RavenDB. För hantering av inloggning och användardata användes Microsoft SQL

2008 6.

Utöver framtagande av en prototyp har mycket av tiden ägnats åt att undersöka hur

andra har använt sig av CQRS och vad de dragit för slutsatser. Det pågår fortfarande

många diskussioner på forum och bloggar där utvecklare har delade meningar, och att

ta in och tolka vad som kan ses som korrekt eller inte har tagit stor del av tiden.

1 https://github.com/tyronegroves/SimpleCQRS 2 https://github.com/joliver/EventStore 3 http://www.asp.net/mvc/mvc4 4 http://signalr.net/ 5 http://ravendb.net/ 6 http:// microsoft.com/sqlserver/

Page 13: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

8

4. Resultat

Följande kapitel tar främst upp hur CQRS, Event Sourcing och Task Based UI

implementeras. Det visade sig vara svårt att hitta bra kodexempel, men utvecklingen av

systemet har försökt att följa teorierna kring arkitekturen så noga som möjligt.

Kodexempel som förekommer i kapitlet är till viss del kopplade till ramverket

SimpleCQRS, men principerna är desamma oavsett ramverk eller om utveckling sker

utan ramverk.

4.1 Implementation av CQRS, Event Sourcing och Task Based UI

4.1.1 Domain Driven Design och Aggregate Roots

För att överhuvudtaget kunna använda sig av CQRS, Task-Based UI och Event

Sourcing krävs att man använder Domain Driven Design (DDD) [2]. Kortfattat

innebär DDD att, istället för att modellera data, modelleras beteenden (metoder) som

återspeglar domänlogiken, det vill säga affärsverksamheten [2] [6]. Figur 2 beskriver

skillnaden på hur modellen för en kund kan se ut i DDD jämfört med CRUD.

Figur 2: CRUD-modell och DDD-modell på en Customer

Page 14: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

9

Inom DDD finns något som kallas ”Aggregates”, aggregat, som är en samling

associerade objekt som behandlas som en enhet [7]. I exemplet i Figur 2 hade, vilket

också är vanligt i en relationsdatabas, kund och dennes adress kunnat hanterats som

två olika entiteter, Customer och Address. En adress är kopplad till en kund, det vill säga

det finns ingen mening för en adress att existera utan koppling till en kund, vilket

innebär att Customer och Address kan ses som en aggregat och att Customer är dess

”Aggregate Root”, aggregatrot. Customer kommer således att exempelvis ha metoderna

AddNewAddress och MoveCustomerToNewAddress för hantering av adresser. DDD säger

också att det endast är aggregatrötterna som utomstående objekt tillåts att hålla

referenser till [7], vilket innebär att det är aggregatrötternas metoder som anropas från

kommandohanterarna när ett kommando skickas i systemet.

I ett system för bokning av passagerare och fordon på färjor kommer Reservation att

vara aggregatrot och bilda aggregat med bland annat entiteterna Passenger och Vehicle,

eftersom det inte finns någon mening med att passagerare och fordon existerar utan

att finnas på en bokning. Reservation kommer att innehålla metoder (hantera beteenden)

så som AddPassengersToReservation och AddVehiclesToReservation. Bilaga 1 visar hur en

aggregat med entiteter kan se ut.

4.1.2 Commands

Ett kommando är en uppmaning som har för avsikt att förändra tillståndet i systemet,

varje kommando tas om hand av en händelsehanterare (command handler). Vanligtvis

skapas ett kommando då det sker en Post, Update eller Delete i systemet, exempelvis vid

postning av ett formulär. Kommandot namnges i imperativ, exempelvis

AddPassengersToReservation, eftersom kommandot uppmanar systemet att förändra något.

Figur 3 visar hur ett kommando kan se ut vars avsikt är att lägga till passagerare på en

bokning.

Figur 3: Exempel på ett kommando

Page 15: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

10

Endast data som är nödvändig för att utföra kommandot skickas, i Figur 3 är det

endast en lista med passagerare som behövs, samt Id på reservationen. I en CRUD –

applikation hade det varit nödvändigt att skicka med all data som har med en

reservation att göra, eftersom modellklasserna endast innehåller egenskaper som

representerar kolumner i tabeller i databasen. Detta medför att om inte all data skickas

med, kommer vissa kolumner i databasen att bli null.

Innan ett kommando skickas ska det vara validerat, att det innehåller korrekt format av

data, vilket vanligtvis i ett första steg görs direkt på klienten, dock även på servern. Ett

kommando ska inte misslyckas för att data som skickas inte är i korrekt format. Figur

4 visar hur validering sker med ASP.NET MVC, innan ett kommando skickas.

Page 16: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

11

Figur 4: Exempel på implementering av validering

Om kommandot är validerat skickas det iväg asynkront och tas emot av en

kommandohanterare. Enligt Dahan är ett bra kommando då systemet kan svara ”Tack!

Ditt bekräftelsemail kommer att anlända inom kort” [8]. Ett kommando kommer med

andra ord aldrig returnera något, vilket gör att de kan skickas asynkront och låta

kommandot utföra dess uppgift i bakgrunden. I Figur 4 returneras null, anledning till

det är att en händelsehanterare kommer att uppdatera gränssnittet när kommandot

genomfört förändringarna, vilket förhoppningsvis sker på några millisekunder. Istället

för null skulle enligt Dahan ett ”Tack-meddelande” visas för användaren.

4.1.3 Command Handlers

Kommandohanterare tar emot kommandon och anropar metoder på aggregatrötterna

(domänen) som genomför de förändringar i domänen som kommandot beordrar, det

vill säga publicerar händelser. Figur 2 visar hur metoder på aggregatrötter kan se ut.

Page 17: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

12

4.1.4 Events

Händelser representerar förändringar i ett systems tillstånd, och publiceras från metoder i

domänen/aggregatrötterna, Figur 2 visar exempel på det. Ett eller flera händelseobjekt

representerar de förändringar som ett kommando vill utföra, med andra ord; händelser

är en bekräftelse på att ett kommando har utförts.

Händelser anges i preteritum, exempelvis PassengersAddedToReservation, för att tydliggöra

att någonting hänt. Dessa tas om hand av händelsehanterare (Event Handlers). Figur 5

visar hur ett händelseobjekt kan se ut.

Figur 5: Exempel på en händelse

Oftast är ett händelseobjekt en kopia på kommandoobjektet, jämför Figur 3 och Figur

5 (ReservationId är satt till AggregateRootId i DomainEvent som alla händelser ärver

ifrån i ramverket SimpleCQRS, se Figur 2 för exempel på det). Det är

händelseobjekten som lagras i händelsedatabasen när Event Sourcing används.

4.1.5 Event Handlers

Händelsehanterare ser till att uppdatera alla ”vyer”, alltså läsmodellerna, i läsdatabasen

som en händelse påverkar, det vill säga uppdatera data som presenteras för

användaren. I en del fall innebär det att flera läsmodeller måste uppdateras beroende

på om en händelse påverkar data i flera ”vyer”.

Ska exempelvis ett bekräftelsemail skickas till en användare är det i eventhanteraren

dessa skickas. Figur 6 visar hur en läsmodell uppdateras och hur en metod på en hub

anropas för att uppdatera data på användarens skärm utan omladdning av sidan.

Page 18: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

13

Figur 6: Exempel på händelsehanterare

4.1.6 Task Based UI

För att kunna spåra vad användaren gör i systemet krävs ett Task Based UI. Task

Based UI är vanligt förekommande, Windows 7 använder sig mycket av det. Figur 7

visar ett exempel på hur ett Task Based UI ser ut i Windows 7.

Figur 7: Exempel på händelsebaserat gränssnitt i Windows 7

Istället för att endast erbjuda en ”ändra-knapp” är det möjligt att spåra användarens

intentioner genom att erbjuda användaren olika alternativ för att utföra olika uppgifter.

Figur 8 visar hur man med ett Task Based UI kan spåra om användaren vill ändra

adress för att denne har flyttat, eller om det är för att korrigera en felaktig adress.

Page 19: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

14

Figur 8: Händelsebaserat gränssnitt för att redigera adress

Båda alternativen kommer att skicka samma data till servern, men via två olika

kommandon, som i sin tur via metoder på domänmodellen kommer att publicera två

olika händelseobjekt, vilket gör att systemet vet exakt vad som hänt och varför. På så

sätt är det möjligt att skilja på om en kund har ändrat en felaktig adress eller har flyttat

och exempelvis skicka ut ett välkomstmeddelande eller erbjudande till nyinflyttade.

4.1.7 Sagas

En saga är till för att hantera ”long-running business transaction” under en

förutbestämd tid [4]. Sagas lyssnar på händelser och skickar kommandon, till skillnad

från aggregater som tar emot kommandon och publicerar händelser [9].

Ett bokningsförfarande är ett exempel på en sådan process. När en användare väljer

vilka platser, alternativt hur många personer, låses de platserna oftast tillfälligt i ett

förutbestämt antal minuter. Anledningen till att platser låses och under en viss tid är

för att, när bokningen väl ska bekräftas, hantera att inte användarens platser har bokats

upp av någon annan under tiden användaren genomfört bokningen. Om användaren

inte bekräftat bokningen under den bestämda tiden kommer bokningen att

ogiltigförklaras och platserna kommer återigen att vara tillgängliga för andra användare

att boka.

Page 20: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

15

Följande scenario kan anses vara rimligt i en bokningsprocess för att boka plats på en

färja. För exemplets skull hålls det enkelt utan val av hytter och dylikt. Publicerade

händelser anges i kursiv.

1. Kunden anger antal passagerare och antal fordon och söker på en avgång,

ReservationCreated, PassengersCountPreliminaryAddedToDeparture,

VehiclesCountPreliminaryAddedToDeparture

2. Kunden väljer avgång: DepartureAddedToReservation,

3. Kunden anger passageraruppgifter: PassengersAddedToReservation

4. Kunden anger registreringsnummer till fordon: VehiclesAddedToReservation

5. Kunden anger kunduppgifter: CustomerAddedToReservation

6. Kunden bekräftar bokning: ReservationConfirmed

7. Kunden betalar bokningen: ReservationPayed

8. Systemet kontrollerar betalning och ”markerar” bokningen som betald och

genomförd och skickar ett bekräftelsemail till kunden: ReservationCompleted,

PreliminaryPassengersCountMadeDefinitely, PreliminaryVehiclesCountMadeDefinitely,

ConfirmationEmailToCustomerSent

Bokningsprocessen sker genom åtta olika steg som sträcker sig över en viss tid, en

”long-running business transaction”. Processen påverkar två olika aggregatrötter,

Reservation och Departure. CQRS säger att ett kommando endast kan påverka en

aggregatrot, men att göra en bokning påverkar antalet bokade på en avgång vilket gör

att en saga krävs.

Som nämndes tidigare är det vanligt att ett bokningssystem preliminär låser antalet

angivna platser i ett förutbestämt antal minuter och har inte bokningen genomförts

ogiltigförklaras den. Genom att implementera en saga som lyssnar på alla händelser i

bokningsprocessen är det möjligt att sätta en ”time out”. När ReservationCreated

publiceras startas sagan och då användaren angett antal passagerare och fordon skickar

sagan kommandon till Departure - aggregaten som i sin tur publicerar

PassengersCountPreliminaryAddedToDeparture, VehiclesCountPreliminaryAddedToDeparture

(dessa görs för att låsa antalet platser och se till att inte avgången blir fullbokad under

tiden bokningen genomförs). När sagan startas sätts en ”time out” på 20 minuter, har

inte ReservationConfirmed (och övriga händelser som krävs) publicerats inom 20 minuter

Page 21: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

16

kommer kommandona CancelReservation, RemovePreliminaryPassengersFromDeparture och

RemovePreliminaryVehiclesFromDeparture att skickas.

Vanligtvis blir en bokning inte genomförd förrän en betalning har inkommit. Då sagan

lyssnar på händelsen ReservationPayed kan sagan skicka kommandona CompleteReservation,

MakePreliminaryPassengerCountDefinitly, MakePreliminaryVehiclesCountDefinitly och

SendConfirmationEmailToCustomer. Skulle inte ReservationPayed inkommit efter, säg 30

minuter efter att händelsen ReservationConfirmed publicerats, körs kommandot

CancelReservation och bokningen blir ogiltigförklarad.

4.2 Flöde CQRS och ASP.NET MVC

Användargränssnittet anropar en metod på en controller som antingen kan returnera

en vymodell och skicka till användargränssnittet, se Figur 9, eller skicka ett

kommando, se Figur 10.

Figur 9: ASP.NET MVC Controller som returnerar en läsmodell från läsdatabasen

Figur 10: ASP.NET MVC Controller som skickar kommando

Page 22: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

17

När kommando skickas kommer en ”command bus” att ta emot och validera

kommandot. Om valideringen går igenom kommer ”command bus” att delegera

vidare kommandot till rätt kommandohanterare som i sin tur kommer att anropa en

metod på aggregatroten och skicka med data från kommandot (ramverket

SimpleCQRS hjälper till med delegeringen från ”command bus” till

kommandohanteraren).

Metoden på aggregatroten publicerar en eller flera händelser med data från

kommandot som representera ändringarna som sker i systemet till följd av

kommandot som skickats. På samma sätt som kommandohanteraren tar om hand om

kommandon tar händelsehanteraren hand om händelser (SimpleCQRS hjälper till med

detta också), vars huvudsyfte är att uppdatera alla läsmodeller, ”vyer”, i läsdatabasen

som påverkas.

Notera att i Figur 10 returneras null från controllern om ett kommando skickas. Ett

kommando returnerar aldrig någonting. Istället för att returnera null är det vanligt att

omdirigera användaren till en konfirmationsmeddelande, men i exemplet i Figur 10

sker ett ajax-anrop och när händelsen har publicerats kommer användargränssnittet att

anropas från händelsehanteraren och uppdatera det med ett bekräftelsemeddelande.

Page 23: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

18

5. Analys

I detta kapitel analyseras de för- och nackdelar om CQRS - arkitekturen som

undersökningen har inbringat.

5.1 Fördelar

CQRS öppnar upp nya möjligheter och förenklar utveckling där det traditionellt sett

kan vara komplext. Med komplext menas inte komplex domänlogik utan teknisk

komplexitet, genom att dela upp läs från skriv försvinner stora delar utav den tekniska

komplexiteten. För att presentera något hämtas data direkt från läsdatabasen och

presenteras, i ”SQL – termer”; SELECT * FROM TABLE WHERE ID = ID, med

andra ord inga sammanslagningar av tabeller och beräkningar eller dylikt som

komplicerar. När affärsverksamheten efter en tid kommer på att ”vi måste presentera

det här värdet här”, kan det ofta, traditionellt sett, innebära att utvecklare måste skriva

om domänmodellerna, i värsta fall ändra i databasen och sedan uppdatera associerad

kod. Med CQRS och Event Sourcing behöver bara händelsehanteraren som påverkas

lägga till värdet i aktuell läsmodell, därefter spela upp alla tillhörande händelser och

värdet är på plats i läsmodellen i läsdatabasen.

Läsdatabasen lagrar data på det sätt det ska presenteras, vilket innebär att relationer

mellan tabeller i databasen inte existerar. Eftersom relationer inte existerar är det inte

nödvändigt att använda sig av en relationsdatabas, exempelvis Microsoft SQL Server. I

prototypen som utvecklades användes en dokumentdatabas, RavenDB, som lagrar

data i formatet JSON i dokument. Dokumentdatabaser strävar efter att hämta data så

effektivt som möjligt, vilket gör att de passar bra som läsdatabaser. Bilaga 2 visar hur

lagringen av vyn ”ReservationOverview”, alltså en bokningsöversikt för en kunds

bokning, lagras i RavenDB.

Att dela upp läs från skriv gör det också möjligt att skala dessa oberoende av varandra

och dela upp de på olika servrar, exempelvis låta läsdatabaserna ligga på tio servrar för

snabb åtkomst av data, medan skrivdatabasen kan ligga på en server. Problemet som

uppstår är att se till att alla läsdatabaser blir uppdaterade när det sker förändringar,

Page 24: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

19

beroende på verksamhet är det mer eller mindre viktigt att läsdatabaserna är

synkroniserade samtidigt.

Att lagra allt som sker i ett system, Event Sourcing, innebär förutom att veta hur och

när data förändras även varför. Eftersom Event Sourcing lagrar allt som sker i ett

system bidrar det till en automatisk logghistorik. Tillsammans med ett väl utfört Task

Based UI får affärsverksamheten exakt koll på vad som har hänt, det vill säga ett fullt

spårningsbart system. Med Event Sourcing blir det också lättare att hitta och spåra

buggar i ett system, eftersom allt som skett har lagrats.

Med Event Sourcing finns möjligheten att ta fram nya, intressanta och informativa

rapporter som inte affärsverksamheten hade planerat från början. Exempelvis kommer

affärsverksamheten bakom bokningssystemet för färjor på att de behöver ta fram

rapport i vilket steg som det är vanligast att användare avbryter bokningen. Eftersom

en boknings byggs upp steg för steg och alla händelser som skett finns lagrade, även

om inte bokningen genomförs fullt ut, blir det väldigt enkelt att ta fram en sådan

rapport.

En av de största fördelarna med CQRS, DDD och Event Sourcing är att utvecklare

kan fokusera på domänen och dess beteende, med andra ord vad affärsverksamheten

ska kunna hantera, samt hur det ska presenteras. Modellering av tabeller och relationer

mellan dessa , optimera och normalisera tabellerna existerar överhuvudtaget inte. Att

optimera för att få snabbare svarstider från databasen existerar inte heller, eftersom

data lagras som det ska presentera hämtas ”vyn” med dess Id, i utvecklartermer

”GetById(Id)”. Däremot kommer redundant data troligtvis att lagras, det vill säga

samma data i flera olika läsmodeller. Att spara ner alla händelser gör också att

datamängden som lagras blir mycket större. Däremot är datalagring idag relativt billigt

samt att utvecklingskostnaden för att ta fram en relationsdatabas försvinner.

Konceptet med DDD och aggregat och aggregatrötter är också något som förenklar

komplexa situationer, då entiteter som hör ihop och dess affärslogik ”samlas” på ett

ställe.

Page 25: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

20

5.2 Nackdelar

Det är lätt att bygga till komplexitet där det inte tidigare fanns. Om det inte är viktigt

för affärsverksamheten att kunna spåra om en användare har korrigerat en felaktig

adress eller ändra på grund av flytt (i de flesta fall är det troligtvis inte särskilt viktigt),

skapar man komplexitet. Att gå igenom alla steg i CQRS, det vill säga skapa ett

kommando, låta en kommandohanterare ta hand om kommandet, anropa en metod på

aggregatroten som i sin tur skapar en händelse som ska publiceras i Event Store och

en händelsehanterare som ser till att vymodellerna i läsdatabasen uppdateras. Många

steg för att uppdatera en adress. Däremot finns det en fördel om det i ett senare skede

av affärsverksamheten blir viktigt att kunna spåra om en användare har ändrat adress,

då allt som skett finns lagrat.

Vilket leder till ett annat problem. Om det från början inte är viktigt för en

affärsverksamhet att veta varför en användare byter adress, så kommer utvecklarna

troligtvis att ha skapa ett och samma händelseobjekt för både användaren har flyttat

och korrigera felaktig adress. Om det i ett senare skede blir viktigt för

affärsverksamheten att skilja på dessa två händelser kommer man ändå inte ha haft det

lagrat från start.

Förutom att redundant data kommer att lagras, blir det kodmässigt en del upprepande.

Ett kommando och händelseobjekt som publiceras i samband med kommandot

innehåller oftast samma data, vilket gör att utvecklare bryter mot DRY, ”Don’t Repeat

Yourself”, alltså att inte upprepa kod. Även utvecklingstiden ökar en aning då det är

fler steg att implementera.

Page 26: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

21

6. Diskussion och slutsats

Syftet med rapporten var att undersöka för- och nackdelar med CQRS och Event

Sourcing jämfört med traditionella CRUD - system men även hur det kan

implementeras, när det kan användas och på vilket sätt man kan spåra vad användarna

gjort i systemet. I detta avslutande kapitel kommer personliga slutsatser att presenteras

utifrån fakta, resultat och analys, samt förslag till vidare forskning.

2009 började Greg Young och sedermera Udi Dahan att hålla föredrag om mönstren

CQRS och Event Sourcing och arkitekturen som kan skapas med dessa. Senaste året

har det börjat få genomslag och på de flesta större utvecklarkonferenser har de dykt

upp föreläsningar om arkitekturen från andra personer.

Som både Dahan [8] [4], Fowler [3] och Abdullin [5] påpekar är inte CQRS och Event

Sourcing till för alla system. Det finns tre scenarior där arkitekturen är användbar. Om

en affärsverksamhet har behovet av att kunna spåra vad användaren gör och föra

historik över det med möjligheten att kunna gå tillbaka till tidigare tillstånd är CQRS

och Event Sourcing en väldigt bra lösning. Om endast systemet behöver hålla historik

över vad som hänt kan det räcka med att lagra loggningshistorik i en separat databas

utan att dra in CQRS och Event Sourcing.

CQRS och Event Sourcing kan vara användbart i de scenarion där

affärsverksamhetens krav och behov ändras efterhand, och där det är viktigt att

förändringar kan ske snabbt [5]. Istället för att behöva modellera om en databas, dess

tabeller och associerad kod vid förändrade krav från affärsverksamheten, är det enkelt

att med CQRS utöka med nya kommandon, händelser och spela upp händelserna igen

och uppdatera läsmodellerna.

CQRS kan förenkla utveckling vid komplexa modeller [3], där obskyra

sammanfogningar och beräkningar mellan tabeller existerar. Med CQRS och Event

Sourcing slipper utvecklare de bitarna. Sällan består ett helt system av komplexa

lösningar, och som Fowler beskriver [3], CQRS ska användas där det finns behov för

det, annars skapas endast komplexitet. Och med uppdelning av läs och skriv är det

Page 27: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

22

möjligt att skala dessa oberoende av varandra passar CQRS exempelvis bra till system

med hög trafik för att snabbt hämta data från läsdatabasen.

Samtidigt kan många av fördelarna lösas med andra sätt. No-SQL databaser börjar få

stort genomslag och tanken med dessa är att lagra denormaliserat data, vilket gör att en

del komplexitet försvinner samtidigt som de är kända för att vara väldigt snabba [10].

Som nämnts tidigare finns det en del diskussionsgrupper på internet angående CQRS

och Event Sourcing, de på Google Groups 7 är mest aktiva, och att det råder så delade

meningar angående mönstret gör att det ibland blir svårt att förstå och veta vad som är

kan anses som korrekt. En annan sak som är något förvirrande är hur Dahan, en av de

främsta förespråkarna, först skriver ett omfattande inlägg om CQRS och dess fördelar

[4] för att sen i ett senare inlägg såga alla fördelar och att utvecklare i princip aldrig ska

använda CQRS [11]. Det finns en del kodexempel tillgängliga på internet, men Dahan

menar på i sitt inlägg att alla exempelapplikationer som finns tillgängliga är fel och att

utvecklare bör undvika att använda ramverken som finns utan att specificera varför.

Att avsluta inlägget med att man borde besöka en av hans workshops för att förstå hur

man gör det korrekt blir väldigt konstigt och känslan av att han enbart vill tjäna

pengar.

Framtagandet av prototypen och resultaten i denna rapport är byggda på ett ramverk

och förståelse för hur saker och ting implementeras har gjorts utifrån exempelkod,

som antagligen Dahan med sitt inlägg menar är icke korrekt. Samtidigt är det smidigt

att använda sig av ett ramverk och det fyller ju faktiskt sin funktion med att förenkla

utvecklandet, och resultatet blir faktiskt att teorierna kring CQRS och Event Sourcing

uppfylls. Med andra ord blir Dahans utspel en aning förvirrande, speciellt när han inte

påpekar konkret vad som är fel, men som med allt inom utveckling av system gäller

det att hitta ett sätt som passar en själv.

7 https://groups.google.com/forum/?fromgroups#!search/cqrs

Page 28: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

23

Som med de flesta mönster, arkitekturer och ramverk finns det för- och nackdelar,

men CQRS och Event Sourcing öppnar faktiskt upp en hel del intressanta möjligheter.

Som nämnts tidigare finns det mycket teori om CQRS men det hade varit intressant se

mer exempel på hur det används i system i praktiken. CQRS har bara ett par år på

nacken och bara det senaste året har det fått mycket uppmärksamhet inom

utvecklarbranschen. Det vore intressant att inom några år ta del av en liknande

undersökning, och se vad som har hänt på vägen och vilket resultat och slutsatser som

dras då. Personligen tror jag att CQRS och Event Sourcing kommer användas än mer

framöver. Något som också är intressant är att Microsofts ”Pattern and practices

team” har ett” open source” - projekt under utveckling 8, vilket framöver kanske leder

till ett lyckat ramverk så som ASP.NET MVC.

8 http://cqrsjourney.github.com/

Page 29: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

24

7. Källförteckning

[1] Martin Fowler. (2005, December) Martin Fowler. [Online].

http://www.martinfowler.com/eaaDev/EventSourcing.html , [Hämtad: 12

april, 2012]

[2] Gregory Young. (2010, February) CodeBetter.com. [Online].

http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-

sourcing-agh/ , [Hämtad: 20 april, 2012]

[3] Martin Fowler. (2011, July) Martin Fowler. [Online].

http://martinfowler.com/bliki/CQRS.html , [Hämtad: 12 april, 2012]

[4] Udi Dahan. (2009, December) Clarified CQRS - Udi Dahan. [Online].

http://www.udidahan.com/2009/12/09/clarified-cqrs/ , [Hämtad: 12 april,

2012]

[5] Rinat Abdullin. (2010, September) Theory of CQRS Command Handlers:

Sagas, ARs and Event Subscriptions. [Online].

http://abdullin.com/journal/2010/9/26/theory-of-cqrs-command-

handlers-sagas-ars-and-event-subscrip.html , [Hämtad: 10 maj, 2012]

[6] Julien Letrouit. (2010, Juli) Transitioning to Event Sourcing, part 1: the

DDD “light” application. [Online]. http://julienletrouit.com/?p=22 ,

[Hämtad: 2 maj, 2012]

[7] Eric Evans, Domain Driven Design - Tackling Complexity in the Heart of Software,

1st ed.: Addison-Wesley Educational Publishers Inc, 2003.

[8] Udin Dahan. (2010, February) Skills Matter - Podcast from London.Net User

Group: Udi Dahan on CQRS. [Online].

http://skillsmatter.com/podcast/open-source-dot-net/udi-dahan-

command-query-responsibility-segregation/rl-311 , [Hämtad: 2 april, 2012]

[9] Jonathan Oliver. (2010, September) CQRS: Sagas with Event Sourcing (Part

I of II). [Online]. http://blog.jonathanoliver.com/2010/09/cqrs-sagas-with-

Page 30: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

25

event-sourcing-part-i-of-ii/ , [Hämtad: 10 maj, 2012]

[10] Jak Charlton. (2010, Oktober) CQRS - The Cult of Shiny Things. [Online].

http://devlicio.us/blogs/casey/archive/2010/10/29/cqrs-the-cult-of-shiny-

things.aspx , [Hämtad: 20 maj, 2012]

[11] Udi Dahan. (2011, April) When to avoid CQRS. [Online].

http://www.udidahan.com/2011/04/22/when-to-avoid-cqrs/ , [Hämtad: 12

april, 2012]

Page 31: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

26

8. Bilagor

8.1 Bilaga 1: <Exempel på aggregatrot med entiteter>

Page 32: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

27

8.2 Bilaga 2: <Lagring av bokningsöversiktsdata i läsdatas>

Page 33: Command Query Responsibility Segregation och …536069/FULLTEXT01.pdfDå CQRS, Event Sourcing och Task Based UI är mycket att läsa in sig på under begränsad tid, föll valet på

351 95 Växjö / 391 82 Kalmar

Tel 0772-28 80 00

[email protected]

Lnu.se