DDD (Domain-Driven Design) verzet zich tegen het idee om één uniform model voor het hele systeem te hebben; In plaats daarvan moedigt het aan het systeem te verdelen in gebonden contexten, die elk hun eigen model hebben. Tijdens de strategische fase van DDD gaat u het bedrijfsdomein in kaart brengen en contextgrenzen voor uw domeinmodellen definiëren.
Tijdens de tactische DDD definieert u uw domeinmodellen nauwkeuriger. De tactische patronen worden toegepast binnen één contextgrens. In een microservicesarchitectuur, waarbij elke gebonden context een microservicekandidaat is, zijn we vooral geïnteresseerd in de entiteit en aggregatiespatronen. Door deze patronen toe te passen, kunnen we natuurlijke grenzen voor de services in onze toepassing identificeren (zie het volgende artikel in deze reeks). Een algemene richtlijn is dat een microservice niet kleiner mag zijn dan een aggregatie en niet groter dan een contextgrens. Eerst bekijken we de tactische patronen. Vervolgens passen we ze toe op de context Verzending gebonden in de Drone Delivery-toepassing.
Overzicht van de tactische patronen
Deze sectie bevat een korte samenvatting van de tactische DDD-patronen. Als u al bekend bent met DDD, kunt u deze sectie waarschijnlijk overslaan. De patronen worden in meer detail beschreven in hoofdstukken 5 – 6 van het boek van Eric Evans, en in Het implementeren van domeingestuurd ontwerp door Vaughn Vernon.
Entiteiten. Een entiteit is een object met een unieke identiteit dat in de loop van de tijd blijft bestaan. Bij een banktoepassing zijn klanten en rekeningen bijvoorbeeld entiteiten.
- Een entiteit heeft een unieke id in het systeem, die kan worden gebruikt om de entiteit op te zoeken of op te halen. Dat betekent niet dat de id altijd rechtstreeks aan gebruikers wordt blootgesteld. Dit kan een GUID of een primaire sleutel in een database zijn.
- Een identiteit kan meerdere gebonden contexten omvatten en kan langer duren dan de levensduur van de toepassing. Bankrekeningnummers of door de overheid uitgegeven id's zijn bijvoorbeeld niet gekoppeld aan de levensduur van een bepaalde toepassing.
- De kenmerken van een entiteit kunnen na verloop van tijd veranderen. De naam of het adres van een persoon kan bijvoorbeeld veranderen, maar ze zijn nog steeds dezelfde persoon.
- Een entiteit kan verwijzingen naar andere entiteiten bevatten.
Waardeobjecten. Een waardeobject heeft geen identiteit. Deze wordt alleen gedefinieerd door de waarden van de kenmerken. Waardeobjecten kunnen ook onveranderbaar zijn. Als u een waardeobject wilt bijwerken, maakt u altijd een nieuw exemplaar om het oude exemplaar te vervangen. Waardeobjecten kunnen methoden bevatten die domeinlogica inkapselen, maar deze methoden mogen geen neveneffecten hebben op de status van het object. Typische voorbeelden van waardeobjecten zijn kleuren, datums en tijden, en valutawaarden.
Aggregaties. Een aggregatie geeft een consistentiegrens rond een of meer entiteiten aan. Precies één entiteit in een aggregaties is de hoofdmap. Opzoeken wordt uitgevoerd met behulp van de id van de hoofdentiteit. Alle andere entiteiten in de aggregaties zijn onderliggende elementen van de hoofdmap en waarnaar wordt verwezen door de aanwijzers uit de hoofdmap te volgen.
Het doel van een aggregatie is om transactionele invarianten te modelleren. In het echt bestaan er complexe relaties tussen dingen. Klanten maken orders, orders bevatten producten, producten hebben leveranciers, enzovoort. Als verschillende verwante objecten in de toepassing worden gewijzigd, hoe wordt dan de consistentie gegarandeerd? Hoe houden we invarianten bij en zien we erop toe?
Traditionele toepassingen hebben vaak databasetransacties gebruikt om consistentie af te dwingen. In een gedistribueerde toepassing is dat echter vaak niet haalbaar. Een enkele zakelijke transactie kan meerdere gegevensarchieven omvatten, of langlopend zijn, of er kunnen services van derden bij betrokken zijn. Uiteindelijk is het aan de toepassing, niet aan de gegevenslaag, om de invarianten af te dwingen die vereist zijn voor het domein. Dat is wat aggregaties zijn bedoeld om te modelleren.
Notitie
Een aggregaties kunnen bestaan uit één entiteit, zonder onderliggende entiteiten. Wat het een aggregatie maakt, is de transactionele grens.
Domein- en toepassingsservices. in DDD-terminologie is een service een object waarmee logica zonder enige status wordt geïmplementeerd. Evans maakt onderscheid tussen domeinservices, die domeinlogica inkapselen en toepassingsservices, die technische functionaliteit bieden, zoals gebruikersverificatie of het verzenden van een sms-bericht. Domeinservices worden vaak gebruikt om gedrag voor meerdere entiteiten te modelleren.
Notitie
De term service is overbelast in softwareontwikkeling. De definitie hier is niet rechtstreeks gerelateerd aan microservices.
Domein gebeurtenissen. domeingebeurtenissen kunnen worden gebruikt om andere onderdelen van het systeem op de hoogte te stellen wanneer er iets gebeurt. Zoals de naam al aangeeft, moeten domeingebeurtenissen iets betekenen in het domein. Zo is 'Er is een record ingevoegd in een tabel' geen domeingebeurtenis. 'Een levering is geannuleerd' is een domeingebeurtenis. Domeingebeurtenissen zijn vooral relevant in een microservicesarchitectuur. Omdat microservices worden gedistribueerd en geen gegevensarchieven delen, is er dankzij domeingebeurtenissen samenwerking mogelijk tussen microservices. Het artikel Interservice-communicatie bespreekt asynchrone berichten in meer detail.
Hier worden enkele andere DDD-patronen vermeld, waaronder factory's, opslagplaatsen en modules. Dit kunnen nuttige patronen zijn voor wanneer u een microservice implementeert, maar ze zijn minder relevant bij het ontwerpen van de grenzen tussen microservices.
Dronelevering: De patronen toepassen
We beginnen met de scenario's die de context met betrekking tot verzending moet verwerken.
- Een klant kan een drone aanvragen om goederen op te halen bij een bedrijf dat is geregistreerd bij de droneleveringsservice.
- De afzender genereert een tag (streepjescode of RFID) die op het pakket moet worden geplaatst.
- Een drone haalt een pakket op van de bronlocatie naar de doellocatie.
- Wanneer een klant een levering plant, biedt het systeem een ETA op basis van routegegevens, weersomstandigheden en historische gegevens.
- Wanneer de drone onderweg is, kan een gebruiker de huidige locatie en de nieuwste ETA volgen.
- Totdat een drone het pakket heeft opgehaald, kan de klant een levering annuleren.
- De klant wordt op de hoogte gesteld wanneer de levering is voltooid.
- De afzender kan een bevestiging van de bezorging aanvragen bij de klant, in de vorm van een handtekening of vingerafdruk.
- Gebruikers kunnen de geschiedenis van een voltooide levering opzoeken.
In deze scenario's heeft het ontwikkelteam de volgende entiteiten geïdentificeerd.
- Levering
- Pakket
- Drone
- Rekening
- Bevestiging
- Melding
- Tag
De eerste vier, Levering, Pakket, Drone en Account, zijn allemaal aggregaties die transactionele consistentiegrenzen vertegenwoordigen. Bevestigingen en Meldingen zijn onderliggende entiteiten van Leveringen, en Tags zijn onderliggende entiteiten van Pakketten.
De waardeobjecten in dit ontwerp omvatten Locatie, ETA, PackageWeight en PackageSize.
Ter illustratie ziet u hier een UML-diagram van de aggregaties Levering. U ziet dat het verwijzingen bevat naar andere aggregaties, waaronder Account, Pakket en Drone.
Er zijn twee domeingebeurtenissen:
Als een drone onderweg is, worden vanuit de entiteit Dronestatus gebeurtenissen verzonden om de locatie en status van de drone te beschrijven (Onderweg, Geland).
Vanuit de entiteit Levering worden steeds LeveringVolgen-gebeurtenissen verzonden wanneer de fase van een levering verandert. De fasen zijn onder meer LeveringGemaakt, LeveringOpnieuwGepland, LeveringOnderweg en LeveringVoltooid.
Zoals u ziet, worden met deze gebeurtenissen zinvolle zaken binnen het domeinmodel beschreven. Er wordt iets beschreven over het domein en dat is niet gebonden aan een bepaalde computertaalconstructie.
Het ontwikkelteam heeft nog een functionaliteitsgebied geïdentificeerd, en dat past niet naadloos in een van de entiteiten die tot nu toe zijn beschreven. Ergens in het systeem moeten alle stappen van het plannen of bijwerken van een levering worden gecoördineerd. Daarom heeft het ontwikkelteam twee domeinservices toegevoegd aan het ontwerp: een Scheduler die de stappen coördineert en een Supervisor die de status van elke stap bewaakt om te detecteren of er stappen zijn mislukt of een time-out is opgetreden. Dit is een variant van het Scheduler Agent Supervisor-patroon.
Volgende stappen
De volgende stap is het definiëren van de grenzen voor elke microservice.