@hschwentner A Day at the Beach " Sea Level # Kite Level ☁ Cloud Level % Fish Level & Clam Level Foto: Dennis Hamilton/flickr/CC BY 2.0 Alistair Cockburn
CUSTOMER TELLS WISH FOR 1 SALES- PERSON SIGNS TO GIVES FOR CONTRACT 3 RISK MANAGER CONTRACT PASSES ON TO 4 CONTRACT VO TES CHECKS CALCU LATES 5 6 7 CALCU- LATES TO 8 2 CAR CREDIT RATING INSTALLMENT CAR RESALE VALUE CONTRACT
26.09.23 //// Seite 375 WPS – Workplace Solutions CUSTOMER TELLS WISH FOR 1 SALES- PERSON SIGNS TO GIVES FOR CONTRACT 3 RISK MANAGER CONTRACT PASSES ON TO 4 CONTRACT VOTES CHECKS CALCULATES 5 6 7 CALCU- LATES TO 8 2 CAR CREDIT RATING INSTALLMENT CAR RESALE VALUE CONTRACT SALES RISK ASSESSMENT
26.09.23 //// Seite 381 WPS – Workplace Solutions Common heuristics: § Points of no Return (persistent work results) § Boundaries within the process § State changes, that affect the “nature” of a work object (e.g. “contract legally binding,” “shopping cart ordered”) § Departments of the organisation § Contextual language § Different usage of work objects § Triggers at different points in time § NOT: by work objects! HOW TO CUT THE DOMAIN? Foto: Wikipedia/PD-ScottForesman
CUSTOMER TELLS WISH FOR 1 SALESPERSON SIGNS TO GIVES FOR CONTRACT 3 RISK MANAGER CONTRACT PASSES ON TO 4 CONTRACT VO TES CHECKS CALCU LATES 5 6 7 CALCU- LATES TO 8 2 CAR CREDIT RATING INSTALLMENT CAR RESALE VALUE CONTRACT SALES RISK ASSESSMENT
CUSTOMER TELLS WISH FOR 1 SALESPERSON SIGNS TO GIVES FOR CONTRACT 3 RISK MANAGER CONTRACT PASSES ON TO 4 CONTRACT VO TES CHECKS CALCU LATES 5 6 7 CALCU- LATES TO 8 2 CAR CREDIT RATING INSTALLMENT CAR RESALE VALUE CONTRACT SALES Group the sentences • Boundary around activites and work objects • Keep actors outside boundaries Give a name to the group RISK ASSESSMENT
Indicators: 1) Actor produces result on their own 2) One-way informa$on flow 3) Different triggers ($me vs. on demand) 4) Ac$vi$es suppor$ng something that is not in the picture 5) Difference in language 6) Different use of the same thing Ask your domain experts!
1) How should it be? 1) Domain Re-Discovery 2) “ideal” context map 2) How is it? 1) Architecture Analysis 2) As-is context map 3) How to move the “is” to the “ideal”? 1) Compare 2) Create List of Refactorings 4) Do the move 1) Extract a suppor$ng domain to learn 2) Then extract core(s)
26.09.23 //// Seite 514 WPS – Workplace Solutions TECHNISCHE SPRACHE Auf den Feature Branch pushen? Den Container hochfahren Ich baue dafür ein Interface
26.09.23 //// Seite 515 WPS – Workplace Solutions TECHNISCHE SPRACHE X Class The picture can’t be displayed. Database Server Client O/R-Mapping Inheritance A B Method Interface Linux Windows Eclipse Visual Studio
26.09.23 //// Seite 526 WPS – Workplace Solutions Fach- sprache { Fach- sprache } UBIQUITOUS LANGUAGE public class SchiffsSilhouette { public void verschiebe { … } public void drehe { … } … }
26.09.23 //// Seite 527 WPS – Workplace Solutions GEMEINSAME SPRACHE § Fachexperten verstehen keine Begriffe zu technischen Umsetzungen § Fachexperten sprechen den Jargon ihrer Domäne, der für Außenstehende wiederum schwer verständlich sein kann è Eine gemeinsame Sprache ist notwendig! § Welche soll es sein? § Die der Entwickler? § Die der Fachexperten? § Etwas dazwischen? èPrinzip von DDD: „Verwende eine Sprache die auf dem Domänenmodell basiert“
26.09.23 //// Seite 528 WPS – Workplace Solutions ALLGEGENWÄRTIGE SPRACHE § Verwende die gemeinsame Sprache in.. § der Kommunikation § mündlich § schriftlich § grafisch § im Code § Im Grunde genommen überall è Deswegen nennt sich die Sprache allgegenwärtig.
26.09.23 //// Seite 529 WPS – Workplace Solutions SPRACHE TAUCHT NICHT EINFACH AUF § Es braucht Wochen bis Monate... § harter Arbeit § und scharfem Fokus § … um die Schlüsselkonzepte offenzulegen. § Die ersten Wörter einer allgegenwärtigen Sprache kommen üblicherweise direkt aus der Domäne § Im Laufe der Entwicklung werden neue Begriffe definiert und hinzugefügt
26.09.23 //// Seite 533 WPS – Workplace Solutions SPRACHEN SIND LEBENDIG § Experimentiere mit alternativen Ausdrucksformen § Das Modell und die Sprache entwickeln sich weiter § Überarbeite dann den Code § Benenne Klassen, Methoden, Module § Entspreche dem neuen Modell § Eine Sprache will gesprochen werden: § Beseitige Unklarheiten durch Konversation
26.09.23 //// Seite 539 WPS – Workplace Solutions GLOSSAR § Fachsprache der Benutzer/Ubiquitous Language •bereits existierende Begriffe •rekonstruierte Begriffe •neue Begriffsbildungen • Wer tut was damit wozu? § Kernkonzepte § Wichtiger am Anfang des Projektes § Oft Wegwerfprodukt
26.09.23 //// Seite 540 WPS – Workplace Solutions GLOSSAR – BEISPIELDOMÄNE SCHACH SPIELER EINE VON ZWEI PERSONEN, DIE FIGUREN AUF DEM BRETT BEWEGT. KÖNIG EINE EINMALIGE FIGUR, DIE NUR EIN FELD PRO ZUG BEWEGT WERDEN KANN. WENN DER KÖNIG SCHACHMATT GESETZT WURDE, IST DAS SPIEL VORBEI. Beschreibung Begriff Oberbegriff Unterscheidungs- merkmale
26.09.23 //// Seite 541 WPS – Workplace Solutions § Ein Team spricht eine eigene Sprache § è Familiensprache, Dialekt § Gleiche Begriffe können (leicht) unterschiedliche Bedeutung haben § Sprache und Modell sind eng gekoppelt JEDER BOUNDED CONTEXT HAT SEINE EIGENE UBIQUITOUS LANGUAGE TIEFEN- MESSUNG PEIL- SCHIFF TIEFEN- ZAHL MARKIER- UNG PEIL- PLAN TIEFEN- ZAHL MANÖVERPLANUNG
26.09.23 //// Seite 542 WPS – Workplace Solutions JEDER BOUNDED CONTEXT HAT SEIN EIGENES GLOSSAR TIEFENZAHL TIEFE AN EINEM BESTIMMTEN ORT UNTER BERÜCKSICHTIGUNG VON EBBE UND FLUT TIEFENZAHL PER ECHOLOT GEMESSENE TIEFE BEI NORMALNULL MANÖVER- PLANUNG TIEFENMESSUNG
26.09.23 //// Seite 544 WPS – Workplace Solutions KERNBEGRIFFE FÜR DAS KINO Saalplan Vorstellung Reservierungs- nummer Kinokarte Saalplanstapel Liste der Reservierungs- nummern Gesamtablauf- plan Tagesablaufplan Film Platz/Sitzplatz Saal/Kinosaal Werbung Veranstaltung Eisverkauf Vorführung ? ?
26.09.23 //// Seite 545 WPS – Workplace Solutions BOUNDED CONTEXTS FÜR DAS KINO Saalplan Vorstellung Reservierungs- nummer Kinokarte Saalplanstapel Liste der Reservierungs- nummern Gesamtablauf- plan Tagesablaufplan Film Platz/Sitzplatz Saal/Kinosaal Werbung Veranstaltung Eisverkauf Vorführung Kartenverkauf Ablaufplanung
26.09.23 //// Seite 546 WPS – Workplace Solutions REDUKTION AUF BOUNDED CONTEXT »KARTENVERKAUF« Saalplan Vorstellung Reservierungs- nummer Kinokarte Saalplanstapel Liste der Reservierungs- nummern Gesamtablauf- plan Tagesablaufplan Film Platz/Sitzplatz Saal/Kinosaal Werbung Veranstaltung Eisverkauf Vorführung Kartenverkauf Ablaufplanung
26.09.23 //// Seite 547 WPS – Workplace Solutions REDUKTION AUF BOUNDED CONTEXT »KARTENVERKAUF« Saalplan Vorstellung Reservierungs- nummer Kinokarte Saalplanstapel Liste der Reservierungs- nummern Film Platz/Sitzplatz Saal/Kinosaal
26.09.23 //// Seite 548 WPS – Workplace Solutions REDUKTION AUF BOUNDED CONTEXT »KARTENVERKAUF« Saalplan Vorstellung Reservierungs- nummer Kinokarte Saalplanstapel Liste der Reservierungs- nummern Film Platz/Sitzplatz Saal/Kinosaal
26.09.23 //// Seite 549 WPS – Workplace Solutions REDUKTION AUF BOUNDED CONTEXT »KARTENVERKAUF« Saalplan Vorstellung Reservierungs- nummer Kinokarte Saalplanstapel Liste der Reservierungs- nummern Film Platz/Sitzplatz Saal/Kinosaal
26.09.23 //// Seite 552 WPS – Workplace Solutions § Durch die Fachexperten (domain experts) § Sie wissen § Worum es bei ihrer Arbeit geht § Wo die Software sie unterstützen kann èWer die Fachlichkeit nicht versteht, kann ihr nicht helfen èSoftwareentwickler und Fachexperten bilden zusammen das Team WIE LERNEN WIR DIE FACHLICHKEIT? Foto: Brandon Raile/Wikipedia
26.09.23 //// Seite 553 WPS – Workplace Solutions WIE KOMMEN WIR AN DAS WISSEN DER FACHEXPERTEN? Techniken: § Interviews § Szenarios § Event Storming § Domain Storytelling Das wollen wir extrahieren! Foto: Brandon Raile/Wikipedia Fach- wissen
26.09.23 //// Seite 554 WPS – Workplace Solutions WORKSHOPS MIT ANWENDERN UND PRODUCT OWNERS “Georgia?” by The Library of Congress, flickr.com • Teilnehmer aus verschiedenen Bereichen (Business, IT, Management, …) • ein Moderator für den Workshop à direktes Feedback von allen Beteiligten
CUSTOMER TELLS WISH FOR 1 SALES-PERSON SIGNS TO GIVES FOR CONTRACT 3 RISIK MANAGER CONTRACT PASSES ON TO 4 CONTRACT VOTES CHECKS CALCULATES 5 6 7 CALCU- LATES TO 8 2 CAR CREDIT RATING INSTALLMENT CAR RESALE VALUE CONTRACT
26.09.23 //// Seite 565 WPS – Workplace Solutions ARCHITECTURE WITHIN A BOUNDED CONTEXT MANEUVER PLANNING User Interface Application Domain Infrastructure
26.09.23 //// Seite 566 WPS – Workplace Solutions DOMAIN-DRIVEN DESIGN—LAYER ARCHITECTURE § User interface layer § Receives input and user commands and presents information. § Application layer (Fowler: Service Layer) § Describes and coordinates application processes. § Domain layer § Represents the business domain logic. § Infrastructure layer § Provides technical services such as persistence or communication with other systems. User Interface Application Domain Infrastructure
26.09.23 //// Seite 569 WPS – Workplace Solutions port port HEXAGONAL ARCHITECTURE Foto: Dennis Hamilton/flickr/CC BY 2.0 http://alistair.cockburn.us/Hexagonal%2Barchitecture adapter adapter Alistair Cockburn
26.09.23 //// Seite 570 WPS – Workplace Solutions KINDS OF PORTS - For UI etc. - Methods to be called - “from above” - For DB and infrastructure - Interfaces to be implemented - “from below”
26.09.23 //// Seite 574 WPS – Workplace Solutions FROM DOMAIN STORY TO DOMAIN MODEL Silhouette MOVES SILHOUETTE NAUTICAL OFFICER LENGTH Length moveBy(:Length) BY 8
26.09.23 //// Seite 577 WPS – Workplace Solutions DOMAIN TERMS – WHAT IS STRIKING? insurance policy vacation request building activity purchase contract order zip code GPS coordinate IBAN container number IATA code
26.09.23 //// Seite 579 WPS – Workplace Solutions ENTITIES § Objects of a domain that the user works with. § The results of the work are reflected by their state! § Can be composed of other entities. § Have an (immutable) identity. § Maintain their own consistency and integrity! § Have a clearly defined life cycle. § Have a (mostly mutable) state. § Describe their state by value objects. § Synonyms: Business Object / Domain Object / Work Material § NOT TO BE confused with the term “ENTITY” from Entity-Relationship-Model! Foto: Bundesrepublik Deutschland/Wikipedia/PD Germany Foto: Kaz/pixabay/CC0 Foto: Thomas G./Wikipedia/CC BY-SA 3.0 Foto: OpenClipart-Vectors/pixabay/CC0
26.09.23 //// Seite 583 WPS – Workplace Solutions VALUE OBJECTS § Value objects are symbols for values of a certain type in the domain. § Symbolize the same value if they are equal. § They are not edited by the user and cannot be modified. § Can be calculated (from other value objects), if necessary. § Can consist of other value objects, but never of entities! ValueObject 2.5 ValueObject ValueObject ValueObject ValueObject two and a half
26.09.23 //// Seite 593 WPS – Workplace Solutions REPOSITORIES § Are the access points to the aggregates! § Encapsulate the technical details of the technical infrastructure layer ... § ... and map external data to entities and value objects. § Store aggregates (e. g. in databases). Repository Aggregate Aggregate Aggregate Aggregate Aggregate
26.09.23 //// Seite 596 WPS – Workplace Solutions AGGREGATES § Aggregates are entities that are relevant in themselves (and not just as part of another entity). § Protect the consistency and integrity of their inner entities. § They don't necessarily have to hide them to do this! § Always have a designated entity as an entry point (root). § Usually stored. § As a whole! § Not necessarily in a relational way! Root Entity VO VO VO Aggregate
26.09.23 //// Seite 597 WPS – Workplace Solutions AGGREGATES § Actually, the value objects are outside as they are immutable and may be reused anywhere. Root Entity VO VO VO Some other object Aggregate
26.09.23 //// Seite 598 WPS – Workplace Solutions AGGREGATES § An aggregate can consist of many entities. Root Entity Entity Entity Entity Entity VO VO VO VO VO VO VO VO VO VO VO Aggregate
26.09.23 //// Seite 624 WPS – Workplace Solutions A BANK ACCOUNT—FIRST DRAFT import org.jmolecules.ddd.annotation.Entity; @Entity public class Account { }
26.09.23 //// Seite 625 WPS – Workplace Solutions A BANK ACCOUNT—FIRST DRAFT import org.jmolecules.ddd.annotation.Entity; @Entity public class Account { }
26.09.23 //// Seite 626 WPS – Workplace Solutions A BANK ACCOUNT—FIRST DRAFT @Entity public class Account { private int _balance; public int getBalance() { return _balance; } public void setBalance(int balance) { _balance = balance; } } ✘ Bad: The account balance can be set to any value
26.09.23 //// Seite 627 WPS – Workplace Solutions A BANK ACCOUNT—SECOND VERSION @Entity public class Account { private int _balance; public int balance() { return _balance; } public void deposit(int amount) { _balance += amount; } public void withdraw(int amount) { _balance -= amount; } } Better: Operations with domain-specific behavior and names
26.09.23 //// Seite 628 WPS – Workplace Solutions A BANK ACCOUNT—THIRD VERSION @Entity public class Account { private int _balance; public int balance() { return _balance; } public void deposit(int amount) { _balance += amount; } public void withdraw(int amount) { if (amount > balance()) { throw new IllegalArgumentException("Amount too big"); } _balance -= amount; } Even better: Preconditions can be checked
26.09.23 //// Seite 631 WPS – Workplace Solutions A BANK ACCOUNT—DESIGN BY CONTRACT USING “ASSERT” @Entity public class Account { // ... public void withdraw(int amount) { assert amount <= balance(); _balance -= amount; } } Assertion using keyword assert
26.09.23 //// Seite 632 WPS – Workplace Solutions A BANK ACCOUNT—DESIGN BY CONTRACT USING “VALID4J” import static org.valid4j.Assertive.*; @Entity public class Account { // ... public void withdraw(int amount) { require(amount <= balance()); _balance -= amount; } } Hamcrest matchers can be used
26.09.23 //// Seite 636 WPS – Workplace Solutions GREAT, BUT… @Entity public class Account { // ... public void withdraw(int amount) { assert amount <= balance(); _balance -= amount; } } Can I withdraw a negative amount? In EUR or GBP or…?
26.09.23 //// Seite 639 WPS – Workplace Solutions AN AMOUNT TYPE import org.jmolecules.ddd.annotation.ValueObject; @ValueObject public class Amount { }
26.09.23 //// Seite 641 WPS – Workplace Solutions AN AMOUNT TYPE @ValueObject public class Amount { private final int _amount; private final Currency _currency; }
26.09.23 //// Seite 642 WPS – Workplace Solutions AN AMOUNT TYPE @ValueObject public class Amount { private final int _amount; private final Currency _currency; public Amount(int amount, Currency currency) { _amount = amount; _currency = currency; } }
26.09.23 //// Seite 647 WPS – Workplace Solutions AN AMOUNT TYPE @ValueObject public class Amount { private final int _amount; private final Currency _currency; public Amount(int amount, Currency currency) { _amount = amount; _currency = currency; } public Amount add(Amount otherAmount) { return Amount.of(_amount + otherAmount._amount, _currency); } } The new value object type behaves correctly
26.09.23 //// Seite 652 WPS – Workplace Solutions A BANK ACCOUNT—FOURTH VERSION @Entity public class Account { private Amount _balance; public Amount balance() { return _balance; } public void deposit(Amount amount) { _balance = _balance.add(amount); } public void withdraw(Amount amount) { assert amount.isLessOrEqual(balance()); _balance = _balance.subtract(amount); } } Now we can use the amount type
26.09.23 //// Seite 653 WPS – Workplace Solutions A BANK ACCOUNT—FOURTH VERSION @Entity public class Account { private Amount _balance; public Amount balance() { return _balance; } public void deposit(Amount amount) { _balance = _balance.add(amount); } public void withdraw(Amount amount) { assert amount.hasSameCurrency(balance()); assert amount.isLessOrEqual(balance()); _balance = _balance.subtract(amount); } } ... and define new contracts
26.09.23 //// Seite 654 WPS – Workplace Solutions A BANK ACCOUNT—EVENT SOURCED @Entity public class Account { private List _transactions; public Amount balance() { // sum up _transactions } public void deposit(Amount amount) { _transactions.append(new Deposition(amount)); } public void withdraw(Amount amount) { assert amount.hasSameCurrency(balance()); assert amount.isLessOrEqual(balance()); _transactions.append(new Withdrawal(amount)); } } History instead of current status
26.09.23 //// Seite 657 WPS – Workplace Solutions IDENTITY OF ENTITIES § In Java, every object has an identity § Namely its reference. § It's a technical identity. § An entity has a identity from a domain perspective § Different technical objects can represent the same entity § In different processes: e.g. on the client / on the server § When they are stored and later read from the database. § The identity can be implemented in two ways: § explicitly (e.g. IBAN) by using a unique key the user is aware of § implicitly (e.g. UUID / GUID) by using a technical key hidden from the user ?
26.09.23 //// Seite 658 WPS – Workplace Solutions A BANK ACCOUNT—WITH EXPLICIT IDENTITY @Entity public class Account { @Identity private final IBAN _iban; public Account(IBAN iban) { _iban = iban; } @Override public boolean equals(Object other) { return _iban.equals(((Account)other)._iban); } // ... } The identity is immutable The key is of course a value object The implementation of equals() is based on the identity
26.09.23 //// Seite 659 WPS – Workplace Solutions HOW CAN A NEW IDENTITY BE CREATED? § Database § Pro: Central § Can sometimes be created “lazily” § A system-wide value type factory § Domain-specific keys known to the user § Often implemented by the repository of a corresponding aggregate type the key is used for § A UUID / GUID generator § Pro: can be generated independently on any server or client § Con: String in a special format
26.09.23 //// Seite 660 WPS – Workplace Solutions BANK TRANSFER—BY DOMAIN SERVICE @Service public class CreditTransferService { public void transfer(Account source, Account destination, Amount amount) { source.withdraw(amount); destination.deposit(amount); } } No fields à stateless
26.09.23 //// Seite 663 WPS – Workplace Solutions EVERY BOUNDED CONTEXT HAS ITS OWN GLOSSARY—REVISITED DEPTH DEPTH AT A SPECIFIC GEO POSITION CONSIDERING THE CURRENT TIDAL LEVEL DEPTH DEPTH BELOW SEA LEVEL MEASURED BY ECHO SOUNDER MANEUVER PLANNING DEPTH MEASUREMENT
26.09.23 //// Seite 664 WPS – Workplace Solutions EVERY BOUNDED CONTEXT HAS ITS OWN IMPLEMENTATION public struct Depth { public Depth( int depthBelowSeaLevel, int tideLevel) { // ... } } MANEUVER PLANNING DEPTH MEASUREMENT public class Depth { public Depth( int centimeter, LocalDate soundingDate) { // ... } }
26.09.23 //// Seite 669 WPS – Workplace Solutions A BANK ACCOUNT—FIRST DRAFT [Entity] public class Account { public int Balance { get; set; } } ✘ Bad: The account balance can be set to any value
26.09.23 //// Seite 670 WPS – Workplace Solutions A BANK ACCOUNT—SECOND VERSION [Entity] public class Account { public int Balance { get; private set; } public void Deposit(int amount) { Balance += amount; } public void Withdraw(int amount) { Balance -= amount; } } Better: Operations with domain-specific behavior and names
26.09.23 //// Seite 671 WPS – Workplace Solutions A BANK ACCOUNT—THIRD VERSION [Entity] public class Account { public int Balance { get; private set; } public void Deposit(int amount) { Balance += amount; } public void Withdraw(int amount) { if (amount > balance()) { throw new ArgumentException("Amount too big"); } Balance -= amount; } Even better: Preconditions can be checked
26.09.23 //// Seite 674 WPS – Workplace Solutions A BANK ACCOUNT—DESIGN BY CONTRACT USING “ASSERT” using static System.Diagnostics.Debug; [Entity] public class Account { // ... public void Withdraw(int amount) { Assert(amount <= balance()); Balance -= amount; } } Assertion using Debug.Assert
26.09.23 //// Seite 677 WPS – Workplace Solutions GREAT, BUT… using static System.Diagnostics.Debug; [Entity] public class Account { // ... public void Withdraw(int amount) { Assert(amount <= balance()); Balance -= amount; } } Can I withdraw a negative amount? In EUR or GBP or…?
26.09.23 //// Seite 681 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public class Amount { private int _amount; private Currency _currency; }
26.09.23 //// Seite 682 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public class Amount { private readonly int _amount; private readonly Currency _currency; }
26.09.23 //// Seite 686 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public class Amount { private readonly int _amount; private readonly Currency _currency; // ... public override bool Equals(object other) => other is Amount && _amount == ((Amount) other)._amount && _currency.Equals(((Amount) other)._currency); }
26.09.23 //// Seite 688 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public class Amount { private readonly int _amount; private readonly Currency _currency; // ... public static bool operator ==(Amount a, Amount b) => a.Equals(b); }
26.09.23 //// Seite 689 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public class Amount { private readonly int _amount; private readonly Currency _currency; // ... public static bool operator ==(Amount a, SignDate b) => a.Equals(b); public static bool operator !=(Amount a, SignDate b) => !a.Equals(b); }
26.09.23 //// Seite 694 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public struct Amount { private readonly int _amount; private readonly Currency _currency; public Amount(int amount, Currency currency) { _amount = amount; _currency = currency; } // Equals() does not have to be overridden }
26.09.23 //// Seite 695 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public struct Amount { private readonly int _amount; private readonly Currency _currency; public Amount(int amount, Currency currency) { _amount = amount; _currency = currency; } // but the operators have to be overridden }
26.09.23 //// Seite 698 WPS – Workplace Solutions AN AMOUNT TYPE—C# 9 AND HIGHER [ValueObject] public record Amount(int amount, Currency currency); // automatically readonly // Equals(), GetHashCode(), ToString(), operators // do not have to be overloaded and work as expected
26.09.23 //// Seite 699 WPS – Workplace Solutions AN AMOUNT TYPE—C# 9 AND HIGHER [ValueObject] public record struct Amount(int amount, Currency currency);
26.09.23 //// Seite 700 WPS – Workplace Solutions AN AMOUNT TYPE—C# 10 AND HIGHER [ValueObject] public readonly record struct Amount(int amount, Currency currency);
26.09.23 //// Seite 701 WPS – Workplace Solutions AN AMOUNT TYPE [ValueObject] public readonly record struct Amount(int _amount, Currency _currency) { public Amount Add(Amount otherAmount) { return Amount.of(_amount + otherAmount._amount, _currency); } } The new value object type behaves correctly
26.09.23 //// Seite 704 WPS – Workplace Solutions A BANK ACCOUNT—FOURTH VERSION [Entity] public class Account { public Amount Balance { get; private set; } public void Deposit(Amount amount) { Balance += amount; } public void Withdraw(Amount amount) { Assert(amount <= Balance); Balance -= amount; } } Now we can use the amount type
26.09.23 //// Seite 705 WPS – Workplace Solutions A BANK ACCOUNT—FOURTH VERSION [Entity] public class Account { public Amount Balance { get; private set; } public void Deposit(Amount amount) { Balance += amount; } public void Withdraw(Amount amount) { Assert(amount.HasSameCurrencyAs(Balance); Assert(amount <= Balance); Balance -= amount; } } ... and define new contracts
26.09.23 //// Seite 706 WPS – Workplace Solutions A BANK ACCOUNT—EVENT SOURCED @Entity public class Account { private List _transactions; public Amount balance() { // sum up _transactions } public void deposit(Amount amount) { _transactions.append(new Deposition(amount)); } public void withdraw(Amount amount) { assert amount.hasSameCurrency(balance()); assert amount.isLessOrEqual(balance()); _transactions.append(new Withdrawal(amount)); } } History instead of current status
26.09.23 //// Seite 709 WPS – Workplace Solutions IDENTITY OF ENTITIES § In Java, every object has an identity § Namely its reference. § It's a technical identity. § An entity has a identity from a domain perspective § Different technical objects can represent the same entity § In different processes: e.g. on the client / on the server § When they are stored and later read from the database. § The identity can be implemented in two ways: § explicitly (e.g. IBAN) by using a unique key the user is aware of § implicitly (e.g. UUID / GUID) by using a technical key hidden from the user ?
26.09.23 //// Seite 710 WPS – Workplace Solutions A BANK ACCOUNT—WITH EXPLICIT IDENTITY @Entity public class Account { @Identity private final IBAN _iban; public Account(IBAN iban) { _iban = iban; } @Override public boolean equals(Object other) { return _iban.equals(((Account)other)._iban); } // ... } The identity is immutable The key is of course a value object The implementation of equals() is based on the identity
26.09.23 //// Seite 711 WPS – Workplace Solutions HOW CAN A NEW IDENTITY BE CREATED? § Database § Pro: Central § Can sometimes be created “lazily” § A system-wide value type factory § Domain-specific keys known to the user § Often implemented by the repository of a corresponding aggregate type the key is used for § A UUID / GUID generator § Pro: can be generated independently on any server or client § Con: String in a special format
26.09.23 //// Seite 712 WPS – Workplace Solutions BANK TRANSFER—BY DOMAIN SERVICE @Service public class CreditTransferService { public void transfer(Account source, Account destination, Amount amount) { source.withdraw(amount); destination.deposit(amount); } } No fields à stateless
26.09.23 //// Seite 715 WPS – Workplace Solutions EVERY BOUNDED CONTEXT HAS ITS OWN GLOSSARY—REVISITED DEPTH DEPTH AT A SPECIFIC GEO POSITION CONSIDERING THE CURRENT TIDAL LEVEL DEPTH DEPTH BELOW SEA LEVEL MEASURED BY ECHO SOUNDER MANEUVER PLANNING DEPTH MEASUREMENT
26.09.23 //// Seite 716 WPS – Workplace Solutions EVERY BOUNDED CONTEXT HAS ITS OWN IMPLEMENTATION public struct Depth { public Depth( int depthBelowSeaLevel, int tideLevel) { // ... } } MANEUVER PLANNING DEPTH MEASUREMENT public class Depth { public Depth( int centimeter, LocalDate soundingDate) { // ... } }
26.09.23 //// Seite 721 WPS – Workplace Solutions A BANK ACCOUNT—FIRST DRAFT #[Entity] class Account { private int $balance; public function getBalance(): int { return $this->balance; } public function setBalance(int $balance): void { $this-> balance = $balance; } } ✘ Bad: The account balance can be set to any value
26.09.23 //// Seite 722 WPS – Workplace Solutions A BANK ACCOUNT—SECOND VERSION #[Entity] class Account { private int $balance; public funtion balance(): int { return $this->balance; } public function deposit(int amount): void { $this->balance += amount; } public function withdraw(int amount): void { $this->balance -= amount; } } Better: Operations with domain-specific behavior and names
26.09.23 //// Seite 723 WPS – Workplace Solutions A BANK ACCOUNT—THIRD VERSION Even better: Preconditions can be checked #[Entity] class Account { private int $balance; public funtion balance(): int { return $this->balance; } public function deposit(int amount): void { $this->balance += amount; } public function withdraw(int amount): void { assert(amount <= $this->balance()); $this->balance -= amount; }
26.09.23 //// Seite 727 WPS – Workplace Solutions GREAT, BUT… #[Entity] class Account { // ... public function withdraw(int $amount): void { assert($amount <= $this->balance()); $this->balance -= $amount; } } Can I withdraw a negative amount? In EUR or GBP or…?
26.09.23 //// Seite 732 WPS – Workplace Solutions AN AMOUNT TYPE #[ValueObject] class Amount { private readonly int $amount; private readonly Currency $currency; }
26.09.23 //// Seite 735 WPS – Workplace Solutions AN AMOUNT TYPE—PHP 8.1 AND HIGHER #[ValueObject] class Amount { public function __construct( private readonly int $amount, private readonly Currency $currency ) {} }
26.09.23 //// Seite 736 WPS – Workplace Solutions AN AMOUNT TYPE—PHP 8.2 AND HIGHER #[ValueObject] readonly class Amount { public function __construct( private int $amount; private Currency $currency; ) {} }
26.09.23 //// Seite 737 WPS – Workplace Solutions AN AMOUNT TYPE #[ValueObject] readonly class Amount { private function __construct( private int $amount, private Currency $currency ) {} public static function of(int $amount, Currency $currency): Amount { return new Amount($amount, $currency); } }
26.09.23 //// Seite 738 WPS – Workplace Solutions AN AMOUNT TYPE #[ValueObject] readonly class Amount { public function __construct( private int $amount; private Currency $currency; ) {} public function equals(Amount $other): bool { return $this->amount === $other->amount && $this->currency->equals($other->currency); } }
26.09.23 //// Seite 741 WPS – Workplace Solutions AN AMOUNT TYPE #[ValueObject] readonly class Amount { public function __construct( private int $amount, private Currency $currency ) {} public function add(Amount $otherAmount): Amount { return Amount.of($this->amount + $otherAmount->amount, $this->currency); } } The new value object type behaves correctly
26.09.23 //// Seite 742 WPS – Workplace Solutions AN AMOUNT TYPE #[ValueObject] readonly class Amount { public function __construct( private int $amount, private Currency $currency ) {} public function add(Amount $otherAmount): Amount { assert($this->hasSameCurrencyAs($otherAmount)); return Amount.of($this->amount + $otherAmount->amount, $this->currency); } public function hasSameCurrencyAs(Amount $otherAmount): bool { return $this->currency === $otherAmount->currency; } } ... and contracts ensure the correct currency
26.09.23 //// Seite 743 WPS – Workplace Solutions A BANK ACCOUNT—FOURTH VERSION Now we can use the amount type #[Entity] class Account { private Amount $balance; public function balance(): Amount { return $this->balance; } public function deposit(Amount $amount): void { $this->balance = $this->balance->add($amount); } public function withdraw(Amount $amount): void { assert($amount.isLessOrEqual($this->balance()); $this->balance = $this->balance->subtract($amount); }
26.09.23 //// Seite 744 WPS – Workplace Solutions #[Entity] class Account { private Amount $balance; public function balance(): Amount { return $this->balance; } public function deposit(Amount $amount): void { $this->balance = $this->balance->add($amount); } public function withdraw(Amount $amount): void { assert $amount.hasSameCurrency($this->balance()); assert($amount.isLessOrEqual($this->balance()); $this->balance = $this->balance->subtract($amount); A BANK ACCOUNT—FOURTH VERSION ... and define new contracts
26.09.23 //// Seite 745 WPS – Workplace Solutions A BANK ACCOUNT—EVENT SOURCED @Entity public class Account { private List _transactions; public Amount balance() { // _transactions aufsummieren } public void deposit(Amount amount) { _transactions.append(new Deposition(amount)); } public void withdraw(Amount amount) { assert amount.hasSameCurrency(balance()); assert amount.isLessOrEqual(balance()); _transactions.append(new Withdrawal(amount)); } } History instead of current status
26.09.23 //// Seite 748 WPS – Workplace Solutions IDENTITY OF ENTITIES § In Java, every object has an identity § Namely its reference. § It's a technical identity. § An entity has a identity from a domain perspective § Different technical objects can represent the same entity § In different processes: e.g. on the client / on the server § When they are stored and later read from the database. § The identity can be implemented in two ways: § explicitly (e.g. IBAN) by using a unique key the user is aware of § implicitly (e.g. UUID / GUID) by using a technical key hidden from the user ?
26.09.23 //// Seite 749 WPS – Workplace Solutions A BANK ACCOUNT—USING A UNIQUE KEY @Entity public class Account { @Identity private final IBAN _iban; public Account(IBAN iban) { _iban = iban; } @Override public boolean equals(Object other) { return _iban.equals(((Account)other)._iban); } // ... } The identity is immutable The key is of course a value object The implementation of equals () is based on the unique key
26.09.23 //// Seite 750 WPS – Workplace Solutions HOW CAN A NEW UNIQUE KEY BE CREATED? § Database § Pro: Central § Can sometimes be created “lazily” § A system-wide value type factory § Domain-specific keys known to the user § Often implemented by the repository of a corresponding aggregate type the key is used for § A UUID / GUID generator § Pro: can be generated independently on any server or client § Con: String in a special format
26.09.23 //// Seite 751 WPS – Workplace Solutions BANK TRANSFER—BY DOMAIN SERVICE @Service public class CreditTransferService { public void transfer(Account source, account destination, Amount amount) { source.withdraw(amount); destination.deposit(amount); } } No fields à stateless
26.09.23 //// Seite 754 WPS – Workplace Solutions EVERY BOUNDED CONTEXT HAS ITS OWN GLOSSARY—REVISITED DEPTH DEPTH AT A SPECIFIC GEO POSITION CONSIDERING THE CURRENT TIDAL LEVEL DEPTH DEPTH BELOW SEA LEVEL MEASURED BY ECHO SOUNDER MANEUVER- PLANNING SOUNDING SERVICE
26.09.23 //// Seite 755 WPS – Workplace Solutions EVERY BOUNDED CONTEXT HAS ITS OWN IMPLEMENTATION public struct Depth { public Depth( int depthBelowSeaLevel, int tideLevel) { // ... } } MANEUVER- PLANNING SOUNDING SERVICE public class Depth { public Depth( int centimeter, LocalDate soundingDate) { // ... } }
@hschwentner Bibliography Brandolini, Alberto. Introducing EventStorming. Self-published, Leanpub. Cockburn, Alistair. “Hexagonal Architecture.” January 4, 2005. https://alistair.cockburn.us/hexagonal-architecture/. Conway, Melvin E. “How Do Committees Invent?” Datamation 14, no. 5 (April 1968): 28–31. Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Boston: Addison-Wesley, 2004. Foote, Brian and Joseph Yoder. “Big Ball of Mud.” PLoP ’97, Monticello, IL, September 1997. Fowler, Martin. Patterns of Enterprise Application Architecture. Boston: Addison- Wesley, 2003. Fowler, Martin. “Strangler Fig Application.” Bliki, June 29, 2004. Hofer, Stefan and Henning Schwentner. Domain Storytelling: a Collaborative, Visual, and Agile Way to Develop Domain-Driven Software. Boston: Addison-Wesley, 2022.