Ga naar hoofdinhoud

Sequentiediagrammen Maken

In dit stuk laten we zien hoe je stap voor stap sequentiediagrammen kunt maken op basis van een domain story, klassendiagram van het domein en pre- en postcondities.

Casusbeschrijving

Als voorbeeld gebruiken we een variant op de Metropolis-casus uit de reader over Domain Storytelling. De kaartverkoop gaat in deze variant via een app in plaats van een persoon.

Scenario: een bezoeker koopt een kaartje voor een film.

Hieronder zie je een fine grained domain story van het happy path van de kaartverkoop.

fine grained domain story

Klassendiagram van het domein

Van bovenstaande domain story hebben we een eerste versie van een klassendiagram van het domein gemaakt.

5127acef52c2715825f73bf726dc7c57

PlantUML broncode voor "klassendiagram domein"
@startuml
hide circle

class Vertoning <<Entity>> {
selecteer()
}

note left of Vertoning::selecteer
Waarschijnlijk wordt
dit een methode
uit de Cinema App
end note

class Stoel <<Entity>> {
isBeschikbaar
reserveer()
}

class Kaartje <<Value Object>> {
titel
datum
starttijd
filmzaal
stoelNummer
genereer()
}

note right of Kaartje
Datatypes kunnen
we later bepalen.
Waarschijnlijk
kunnen dit Value Objects
worden
end note

note bottom of Kaartje
Constructor?
end note

Vertoning "1" --> "stoelen *" Stoel


@enduml

Klassendiagram met 3 klasse(n) en 1 relatie(s).

Klassen:

  • Klasse Vertoning met stereotype Entity met:
    • publieke methode 'selecteer', zonder parameters, return type void
    • geen attributen
  • Klasse Stoel met stereotype Entity met:
    • publieke methode 'reserveer', zonder parameters, return type void
    • publieke attribuut 'isBeschikbaar'
  • Klasse Kaartje met stereotype Value Object met:
    • publieke methode 'genereer', zonder parameters, return type void
    • publieke attribuut 'titel'
    • publieke attribuut 'datum'
    • publieke attribuut 'starttijd'
    • publieke attribuut 'filmzaal'
    • publieke attribuut 'stoelNummer'

Relaties:

  • Vertoning heeft een associatie-relatie met naam 'stoelen *' met Stoel

Notities:

  • Bij klasse Vertoning: ":selecteer"

Pre- en postcondities:

Preconditie:

  • Er zijn beschikbare stoelen voor de vertoning

Postcondities:

  • De gereserveerde stoel is niet meer beschikbaar voor een andere gebruiker
  • Er is een kaartje aangemaakt

Sequentiediagrammen maken

Je kunt één sequentiediagram maken van de domain story, maar dan wordt het sequentiediagram vrij groot. Daarom maken we voor elke actie die de bezoeker initieert een apart sequentiediagram. In dit geval maken we een sequentiediagram voor:

  1. Beschikbare stoelen vinden
  2. Stoel reserveren

Voordat we een sequentiediagram maken, inventariseren we eerst welke van deze pre- en postcondities relevant zijn voor de actie die je wilt uitbeelden.

Opmerking

In een groter project maak je vaak eerst user stories voordat je sequentiediagrammen maakt. In dit geval zouden "Beschikbare stoelen vinden" en "Stoel reserveren" twee user stories kunnen worden. Het vinden van user stories op basis van domain stories behandelen we in week 4.

Sequentiediagram 1: Beschikbare stoelen vinden

We maken een sequentiediagram van zin 1 en 2 zoals onderstaande domain story toont:

Beschikbare stoelen vinden

Pre- en postcondities

Voor dit sequentiediagram geldt alleen de preconditie dat er beschikbare stoelen zijn. Er geldt geen postconditie, want dit diagram beschrijft alleen het ophalen van informatie zonder de applicatie te wijzigen.

Actoren

In de eerste stap neem je de actoren uit de domain story op en bepaal je welke informatie ze uitwisselen. We hebben twee actoren:

  • Bezoeker
  • CinemaApp

47c46eefdc199b24e9c0354847ea1ac8

PlantUML broncode voor "actoren"
@startuml

title Bezoeker communiceert \n\
met CinemaApp

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app

@enduml

Sequentiediagram met 2 deelnemers: Bezoeker en app van het type CinemaApp.

Nu bedenken we welke informatie de Bezoeker aan de Cinema App moet geven. We bedenken (op basis van onze ervaring met het maken van web applicaties) dat het handig is als de gebruiker een id van de bedoelde vertoning kan doorgeven aan de app en we bedenken de methode-naam selecteerVertoning die de intentie van de Bezoeker goed weergeeft

83fe0e85bca474b444bc4fff6bb99ee9

PlantUML broncode voor "eerste bericht"
@startuml

title Bezoeker communiceert \n\
met CinemaApp

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app

Bezoeker -> app : selecteerVertoning(id)

@enduml

Sequentiediagram met 2 deelnemers: Bezoeker en app van het type CinemaApp.

Interacties:

  1. Bezoeker roept app.selecteerVertoning(id) aan
Opmerking over UI

Er is een grote kans dat de Bezoeker in een browser, of app op een knop drukt die gekoppeld is aan een url met een id van de geselecteerde vertoning. Probeer (net als in een domain story) geen interactie met de user interface op te nemen omdat dit te gedetailleerd is.

Opmerking over url

De Bezoeker roept de methode selecteerVertoning waarschijnlijk niet direct aan. In plaats van deze methode-aanroep kun je ook een URL of een REST-route opnemen, zoals hieronder te zien is.

64656a5a118a9cb9a606d318b5a1aa95

PlantUML broncode voor "geen rest"
@startuml 

title Bezoeker communiceert \n\
met CinemaApp

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app

Bezoeker -> app : GET vertoningen/id


@enduml

Sequentiediagram met 2 deelnemers: Bezoeker en app van het type CinemaApp.

Interacties:

  1. Bezoeker roept app.GET vertoningen/id() aan

Dit is echter ook een vrij gedetailleerde beslissing die we nu nog niet willen nemen. In week 3 doen we dit wel omdat we dan bestaande code willen analyseren met een sequentiediagram.

Update klassendiagram

Op basis van deze eerste pijl uit het sequentiediagram kunnen we het klassendiagram van het domein als volgt aanpassen:

98bbf937aa9ab54b00e30a6cc13326dd

PlantUML broncode voor "klassendiagram domein versie 2"
@startuml
hide circle

class Vertoning <<entity>> {
id
<strike>selecteer()</strike>
}

note right of Vertoning::id
Toegevoegd
end note

note left of Vertoning::selecteer
Is inderdaad
niet nodig
end note

class Stoel <<entity>> {
isBeschikbaar
reserveer()
}

note left of Stoel::isBeschikbaar
Toevoeging
vanwege
post-conditie
end note

class Kaartje <<Value Object>> {
titel
datum
starttijd
filmzaal
stoelNummer
genereer()
}

Vertoning "1" --> "stoelen *" Stoel

@enduml

Klassendiagram met 3 klasse(n) en 1 relatie(s).

Klassen:

  • Klasse Vertoning met stereotype entity met:
    • publieke methode 'selecteer', zonder parameters, return type
    • publieke attribuut 'id'
  • Klasse Stoel met stereotype entity met:
    • publieke methode 'reserveer', zonder parameters, return type void
    • publieke attribuut 'isBeschikbaar'
  • Klasse Kaartje met stereotype Value Object met:
    • publieke methode 'genereer', zonder parameters, return type void
    • publieke attribuut 'titel'
    • publieke attribuut 'datum'
    • publieke attribuut 'starttijd'
    • publieke attribuut 'filmzaal'
    • publieke attribuut 'stoelNummer'

Relaties:

  • Vertoning heeft een associatie-relatie met naam 'stoelen *' met Stoel

Notities:

  • Bij klasse Vertoning: ":id"
  • Bij klasse Vertoning: ":selecteer"
  • Bij klasse Stoel: ":isBeschikbaar"

Cinema App zoekt vertoning

De Cinema App moet een vertoning-object vinden zodat deze teruggegeven kan worden aan de Bezoeker. Het is waarschijnlijk dat deze vertoning uit een database gehaald wordt op basis van het id. We willen ons op dit moment echter nog niet verbinden aan een specifieke database en daarom gebruiken een meer algemene Store waarbij we er vanuit gaan dat er een methode bestaat findVertoning die een id meekrijgt. Deze methode retourneert een Vertoning-object die we opvangen in de variabele vertoning.

Het sequentiediagram ziet er dan als volgt uit:

9bc1dbea7473d2ae30ce81005d74207a

PlantUML broncode voor "app zoekt vertoning"
@startuml

Title CinemaApp moet een \n\
vertoning kunnen vinden

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app
database Store

Bezoeker -> app : selecteerVertoning(id)

app -> Store : vertoning = findVertoning(id)

@enduml

Sequentiediagram met 3 deelnemers: Bezoeker, app van het type CinemaApp en Store.

Interacties:

  1. Bezoeker roept app.selecteerVertoning(id) aan
  2. app roept Store.vertoning = findVertoning(id) aan
Opmerking Database

Waarschijnlijk wordt de Store uiteindelijk een SQL-database. In dat geval kun je ook zoiets als het onderstaande tekenen:

5b5102abb7a185f08f21d9fd0d04cf0d

PlantUML broncode voor "sql"
@startuml

Title CinemaApp moet een \n\
vertoning kunnen vinden

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app
database Database

Bezoeker -> app : selecteerVertoning( \n\
id)

app -> Database: vertoning = \n\
SELECT * FROM vertoningen \n\
WHERE vertoningId = id

@enduml

Sequentiediagram met 3 deelnemers: Bezoeker, app van het type CinemaApp en Database.

Interacties:

  1. Bezoeker roept app.selecteerVertoning( \n\() aan
  2. app roept Database.vertoning = \n\() aan

Ook dit is te gedetailleerd.

Beschikbare stoelen vinden

Omdat een vertoning een lijst van stoelen bijhoud, zoals te zien in het klassendiagram, verzinnen we dat het handig is om een methode te hebben die alle beschikbare stoelen teruggeeft.

55ca049477eae1bd9a66b9a34210775b

PlantUML broncode voor "beschikbare stoelen vinden"
@startuml

title CinemaApp vindt beschikbare stoelen

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app
participant "vertoning:\nVertoning" as vertoning
database Store

Bezoeker -> app : selecteerVertoning( \n\
id)

app -> Store : vertoning = findVertoning(\n\
id)
app -> vertoning : beschikbareStoelen = \n\
getBeschikbareStoelen()

Bezoeker <-- app: beschikbareStoelen

@enduml

Sequentiediagram met 4 deelnemers: Bezoeker, app van het type CinemaApp, vertoning van het type Vertoning en Store.

Interacties:

  1. Bezoeker roept app.selecteerVertoning( \n\() aan
  2. app roept Store.vertoning = findVertoning(\n\() aan
  3. app roept vertoning.beschikbareStoelen = \n\() aan

Je ziet in bovenstaand diagram twee manieren om een returnwaarde aan te geven. In pijl 2 en 3 zie je de korte manier. Je kunt deze manier gebruiken als de returnwaarde gelijk beschikbaar is en je geen extra acties hoeft te ondernemen om de returnwaarde te krijgen.

Pijl 4 kan gezien worden als de return-waarde van pijl 1. Omdat tussen pijl 1 en 4 twee tussenstappen zitten, gebruiken we de langere manier.

Update klassendiagram van het domein

We voegen de methode getBeschikbareStoelen toe.

Om te zien of een stoel beschikbaar is, voegen we een attribuut isBeschikbaar toe aan de klasse Stoel.

19ec0c4c63434b3134c31f12d01508b2

PlantUML broncode voor "klassendiagram domein versie 3"
@startuml
hide circle

class Vertoning <<Entity>> {
id
getBeschikbareStoelen()
}

note left of Vertoning::getBeschikbareStoelen()
Toegevoegd
end note

class Stoel <<Entity>> {
isBeschikbaar
reserveer()
}

note left of Stoel::isBeschikbaar
Toegevoegd
end note

class Kaartje <<Value Object>> {
titel
datum
starttijd
filmzaal
stoelNummer
genereer()
}

Vertoning "1" --> "stoelen *" Stoel

@enduml

Klassendiagram met 3 klasse(n) en 1 relatie(s).

Klassen:

  • Klasse Vertoning met stereotype Entity met:
    • publieke methode 'getBeschikbareStoelen', zonder parameters, return type void
    • publieke attribuut 'id'
  • Klasse Stoel met stereotype Entity met:
    • publieke methode 'reserveer', zonder parameters, return type void
    • publieke attribuut 'isBeschikbaar'
  • Klasse Kaartje met stereotype Value Object met:
    • publieke methode 'genereer', zonder parameters, return type void
    • publieke attribuut 'titel'
    • publieke attribuut 'datum'
    • publieke attribuut 'starttijd'
    • publieke attribuut 'filmzaal'
    • publieke attribuut 'stoelNummer'

Relaties:

  • Vertoning heeft een associatie-relatie met naam 'stoelen *' met Stoel

Notities:

  • Bij klasse Vertoning: ":getBeschikbareStoelen()"
  • Bij klasse Stoel: ":isBeschikbaar"
Opmerking

In plaats van het attribuut isBeschikbaar toe te voegen, hadden we de lijst stoelen kunnen splitsen in een lijst van beschikbare stoelen en een lijst van gereserveerde stoelen. Het diagram zou er dan zo uitzien:

a729f1c259538f7d85a931163b0170cf

PlantUML broncode voor "klassendiagram domein versie 3 alternatief"
@startuml
hide circle

class Vertoning <<entity>> {
id
getBeschikbareStoelen()
}

class Stoel <<entity>> {
reserveer()
}

class Kaartje <<Value Object>> {
titel
datum
starttijd
filmzaal
stoelNummer
genereer()
}

Vertoning "1" --> "beschikbareStoelen \n*" Stoel
Vertoning "1" --> "gereserveerdeStoelen \n*" Stoel

@enduml

Klassendiagram met 3 klasse(n) en 2 relatie(s).

Klassen:

  • Klasse Vertoning met stereotype entity met:
    • publieke methode 'getBeschikbareStoelen', zonder parameters, return type void
    • publieke attribuut 'id'
  • Klasse Stoel met stereotype entity met:
    • publieke methode 'reserveer', zonder parameters, return type void
    • geen attributen
  • Klasse Kaartje met stereotype Value Object met:
    • publieke methode 'genereer', zonder parameters, return type void
    • publieke attribuut 'titel'
    • publieke attribuut 'datum'
    • publieke attribuut 'starttijd'
    • publieke attribuut 'filmzaal'
    • publieke attribuut 'stoelNummer'

Relaties:

  • Vertoning heeft een associatie-relatie met naam 'beschikbareStoelen' met Stoel, multipliciteit 1 naar *
  • Vertoning heeft een associatie-relatie met naam 'gereserveerdeStoelen' met Stoel, multipliciteit 1 naar *

Er is op dit moment niet echt een goede reden om voor het een of het ander te kiezen, dus we kiezen nu voor de eerste optie omdat dit simpeler is.

Pre en postcondities controleren

De preconditie dat er beschikbare stoelen zijn is compatibel met het sequentiediagram. Er geldt geen postconditie.

Sequentiediagram 2: Stoel reserveren

We maken een sequentiediagram van zin 3 en 4. Onderstaande domain story toont deze twee 'zinnen':

Activity 3 en 4

Pre en poscondities

Voor dit sequentiediagram gelden alle pre- en postcondities.

Preconditie:

  • Er zijn beschikbare stoelen voor de vertoning

Postcondities:

  • De reserveerde stoel is niet meer beschikbaar voor een andere gebruiker
  • Er is een kaartje aangemaakt

Stoel en vertoning vinden

In de eerste stap bedenk je welke informatie de Bezoeker aan de App geeft en welke methode-naam je daarvoor wilt gebruiken. De Cinema App moet weten welke stoel van welke vertoning de bezoeker wil reserveren. Het sequentiediagram ziet er zo uit:

306da9521127c1902d3b32906205e75b

PlantUML broncode voor "stoel reserveren"
@startuml

title Stoel reserveren

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app
participant "vertoning:\nVertoning" as vertoning
participant "stoel:\nStoel" as stoel
database Store

Bezoeker -> app : reserveerStoel(\n\
stoelNummer, id)

app -> Store : vertoning = findVertoning(id)

@enduml

Sequentiediagram met 5 deelnemers: Bezoeker, app van het type CinemaApp, vertoning van het type Vertoning, stoel van het type Stoel en Store.

Interacties:

  1. Bezoeker roept app.reserveerStoel(\n\() aan
  2. app roept Store.vertoning = findVertoning(id) aan

1 Omdat stoelen onderdeel zijn van een vertoning, kiezen we er nu voor om stoelen te reserveren via de vertoning. Dat betekent dat een stoelNummer niet uniek is binnen de hele applicatie en we alleen de juiste stoel kunnen vinden door een id van de vertoning en een stoelNummer mee te geven.

2 We maken de sequentiediagrammen onafhankelijk van elkaar, dus in dit diagram staat ook dat de applicatie de juiste vertoning moet vinden.

Update klassendiagram van het domein

c6dbb72fc529000573ad7bcb142acaaf

PlantUML broncode voor "klassendiagram domein versie 4"
@startuml
hide circle

class Vertoning <<entity>> {
id
getBeschikbareStoelen()
}

note right of Vertoning::selecteer
Is inderdaad
niet nodig
end note

class Stoel <<entity>> {
stoelNummer
isBeschikbaar
reserveer()
}

note left of Stoel::stoelNummer
Toegevoegd
end note

class Kaartje <<Value Object>> {
titel
datum
starttijd
filmzaal
stoelNummer
genereer()
}

Vertoning "1" --> "stoelen *" Stoel

@enduml

Klassendiagram met 3 klasse(n) en 1 relatie(s).

Klassen:

  • Klasse Vertoning met stereotype entity met:
    • publieke methode 'getBeschikbareStoelen', zonder parameters, return type void
    • publieke attribuut 'id'
  • Klasse Stoel met stereotype entity met:
    • publieke methode 'reserveer', zonder parameters, return type void
    • publieke attribuut 'stoelNummer'
    • publieke attribuut 'isBeschikbaar'
  • Klasse Kaartje met stereotype Value Object met:
    • publieke methode 'genereer', zonder parameters, return type void
    • publieke attribuut 'titel'
    • publieke attribuut 'datum'
    • publieke attribuut 'starttijd'
    • publieke attribuut 'filmzaal'
    • publieke attribuut 'stoelNummer'

Relaties:

  • Vertoning heeft een associatie-relatie met naam 'stoelen *' met Stoel

Notities:

  • Bij klasse Vertoning: ":selecteer"
  • Bij klasse Stoel: ":stoelNummer"

Stoel reserveren

Eerder hebben we gezien dat we een Store kunnen gebruiken en ervan uit kunnen gaan dat deze een find methode heeft. Je kunt ook een find methode gebruiken als het ene object een verzameling, of lijst van een ander object bijhoudt. Omdat in het klassendiagram Vertoning een lijst van Stoel bijhoudt, kunnen we er in het sequentiediagram vanuit gaan dat er findStoel methode bestaat in Vertoning.

ffc4bfd8905b5ad758b4bc57dac1b960

PlantUML broncode voor "systeem reserveert stoel optie 1"
@startuml

title CinemaApp reserveert stoel

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app
participant "vertoning:\nVertoning" as vertoning
participant "stoel:\nStoel" as stoel
database Store

Bezoeker -> app : reserveerStoel(\n\
stoelNummer, id)

app -> Store : vertoning = findVertoning(id)

app -> vertoning: stoel = findStoel(stoelNummer)

app -> stoel : reserveer()

stoel -> stoel: isBeschikbaar(false)

@enduml

Sequentiediagram met 5 deelnemers: Bezoeker, app van het type CinemaApp, vertoning van het type Vertoning, stoel van het type Stoel en Store.

Interacties:

  1. Bezoeker roept app.reserveerStoel(\n\() aan
  2. app roept Store.vertoning = findVertoning(id) aan
  3. app roept vertoning.stoel = findStoel(stoelNummer) aan
  4. app roept stoel.reserveer() aan
  5. stoel roept stoel.isBeschikbaar(false) aan

Stoel reserveren alternatief

In onderstaand alternatief reserveert vertoning de juiste stoel in plaats van CinemaApp. Hierdoor hoeft de app niet eerst een Stoel-object te vinden. Tijdens de les bepalen we of één van de alternatieven de voorkeur heeft.

❓ Heb je nu al voorkeur voor het eerste of het tweede alternatief?

5632d5228c26a901e9d652bf19f32bbc

PlantUML broncode voor "systeem reserveert stoel optie 2"
@startuml

title Vertoning reserveert stoel

autonumber

actor Bezoeker
participant "app:\nCinemaApp" as app
participant "vertoning:\nVertoning" as vertoning
participant "stoel:\nStoel" as stoel
database Store

Bezoeker -> app : reserveerStoel(\n\
stoelNummer, id)

app -> Store : vertoning = findVertoning(id)
app -> vertoning: reserveerStoel(stoelNummer)
vertoning -> vertoning: stoel = findStoel(stoelNummer)

vertoning -> stoel : reserveer()

stoel -> stoel: isBeschikbaar(false)

@enduml

Sequentiediagram met 5 deelnemers: Bezoeker, app van het type CinemaApp, vertoning van het type Vertoning, stoel van het type Stoel en Store.

Interacties:

  1. Bezoeker roept app.reserveerStoel(\n\() aan
  2. app roept Store.vertoning = findVertoning(id) aan
  3. app roept vertoning.reserveerStoel(stoelNummer) aan
  4. vertoning roept vertoning.stoel = findStoel(stoelNummer) aan
  5. vertoning roept stoel.reserveer() aan
  6. stoel roept stoel.isBeschikbaar(false) aan

Postcondities checken

Preconditie:

✔️ Er zijn beschikbare stoelen voor de vertoning.

Postcondities:

✔️ De gereserveerde stoel is niet meer beschikbaar voor een andere bezoeker.
❌ Er is een kaartje aangemaakt.

Kaartje is niet aangemaakt, dus het sequentiediagram is nog niet af, maar dat doen we in de les.