Tactische DDD gebruiken om microservices te ontwerpen

Azure Migrate

Tijdens de strategische fase van domain-driven design (DDD) wijst u het bedrijfsdomein uit en definieert u gebonden contexten voor uw domeinmodellen. Tijdens de tactische DDD definieert u uw domeinmodellen nauwkeuriger. De tactische patronen worden toegepast binnen één contextgrens. In een microservicesarchitectuur zijn we vooral geïnteresseerd in de entiteits- en aggregatiepatronen. 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 Verzendingsgrens in de Drone Delivery-toepassing.

Overzicht van de tactische patronen

Deze sectie bevat een kort overzicht van de tactische DDD-patronen, dus als u al bekend bent met DDD, kunt u deze sectie waarschijnlijk overslaan. De patronen worden in meer detail beschreven in hoofdstuk 5 – 6 van Eric Evans' boek, en in Implementing Domain-Driven Design van Vaughn Vernon.

Diagram van tactische patronen in domeingestuurd ontwerp

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 beschikbaar is voor gebruikers. Dit kan een GUID of een primaire sleutel in een database zijn.
  • Een identiteit kan meerdere 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 het is nog steeds dezelfde persoon.
  • Een entiteit kan verwijzingen naar andere entiteiten bevatten.

Waardeobjecten. Een waardeobject heeft geen identiteit. Het wordt alleen gedefinieerd door de waarden van de kenmerken. Waardeobjecten zijn ook onveranderbaar. Als u een waardeobject wilt bijwerken, maakt u altijd een nieuw exemplaar om het oude exemplaar te vervangen. Waardeobjecten kunnen methoden hebben 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 aggregatie is de hoofdmap. Opzoeken wordt uitgevoerd met behulp van de id van de hoofdentiteit. Alle andere entiteiten in de aggregatie zijn onderliggende elementen van de hoofdmap en er wordt naar verwezen door de volgende 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, kan langdurig zijn of services van derden omvatten. 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 moeten modelleren.

Notitie

Een aggregatie kan 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. In het artikel Interservicecommunicatie wordt asynchrone berichten uitgebreider besproken.

Er zijn enkele andere DDD-patronen die hier niet worden 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 door de context Verzendgrens moeten worden afgehandeld.

  • Een klant kan een drone vragen om goederen op te halen bij een bedrijf dat is geregistreerd bij de droneleveringsservice.
  • De afzender genereert een tag (streepjescode of RFID) om op het pakket te plaatsen.
  • Een drone haalt een pakket van de bronlocatie op en levert het af op de bestemmingslocatie.
  • Wanneer een klant een levering plant, biedt het systeem een ETA op basis van routegegevens, weersomstandigheden en historische gegevens.
  • Wanneer de drone in vlucht is, kan een gebruiker de huidige locatie en de meest recente ETA volgen.
  • Totdat een drone het pakket heeft opgehaald, kan de klant een levering annuleren.
  • De klant krijgt een melding wanneer de levering is voltooid.
  • De afzender kan de leveringsbevestiging van de klant aanvragen, in de vorm van een handtekening of vingerafdruk.
  • Gebruikers kunnen de geschiedenis van een voltooide levering opzoeken.

Op basis van deze scenario's heeft het ontwikkelteam de volgende entiteiten geïdentificeerd.

  • Levering
  • Pakket
  • Drone
  • Account
  • 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 zijn Location, ETA, PackageWeight en PackageSize.

Ter illustratie volgt hier een UML-diagram van de aggregatie Levering. U ziet dat het verwijzingen bevat naar andere aggregaties, waaronder Account, Package en Drone.

UML-diagram van de aggregatie Levering

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 dat er een time-out optreedt. Dit is een variatie op het patroon Scheduler Agent Supervisor.

Diagram van het herziene domeinmodel

Volgende stappen

De volgende stap is het definiëren van de grenzen voor elke microservice.