Aggregate Design: Using Invariants as a Guide

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what are aggregates well they're a collection of domain objects that form a boundary i talk a lot about boundaries on this channel and aggregates are just another way to define them i'll explain how i think about aggregates how i design them in an example and code hey everybody it's derek coverton from codeopinion.com if you're new to my channel i post videos on software architecture and design so if you're into those topics make sure to subscribe so for this example i'm going to be using the concept of a shipment you could think of this as even food delivery like a food delivery order and that would be our shipment so you have somebody that is going to go and pick up the food that's going to be a stop that's your first stop this pickup and then the second stop is the delivery where you're dropping off the food at say somebody's house so that's the example i'm going to be using throughout this so the key thing about a stop whether it's a pickup or a delivery is there's three initial states the first state is going to be in transit that means that we are on our way to a particular stop whether that be even something down the line in the future the next state is arrived that means that we arrived at the particular stop and we are either doing our pickup or our delivery departed states means we left and we're on our way to the next stop so those are our three states that a stop can be in so here's what that flow would actually look like of those straight state transitions for our pickup and our delivery so this first starts our pickup the last stop here is our delivery and both are in and in transit state the flow of this is going to be that the first stop the pickup is going to then we're going to arrive then we're going to depart then we're going to arrive the delivery then we're going to part delivery at this point our shipment is done so how do aggregates help well as i mentioned in the very beginning is that the our collection of domain objects and as i've shown we actually kind of have three we have our shipment itself and we have two types of stops we have a pickup stop and a delivery stop and the way i like to think about aggregates and themselves is that ken stops the pickup delivery live without the shipment intuitively you might think immediately no they can't because the actual stops are directly related to the shipment and that's true however the real kind of more deep-seated reason why you want to use an aggregate here are because of invariance invariants are business rules that must always be consistent so this means that our collection of domain objects are aggregate need to maintain that consistency and apply those business rules so the business rules for our shipment really revolve around the stops now the thing is is that i mentioned that the stops have this state transition of going from in transit arrive departed now we can apply that business rule to the stop itself to make sure that it goes through um this progression however what happens if we have our two stops and we know that we have to go from in transit arrive departed but what happens if we try to do that on the delivery before the pickup well we can't do that but the problem here is that the delivery and the pickup don't know about each other so a way to enforce this invariant of that a stop has to go through a state progression but as well as the order of the stops have to be through a certain progression meaning we have to do our pickup first then the delivery is through our aggregate and our aggregate's going to consist of the shipment and the stops now the key point here is that our shipment becomes the aggregate route now the aggregate root is what i like to think of is the gateway meaning all the interactions and everything that we're going to expose in terms of methods or behaviors are going to be done on the shipment no other calling code or consumers are going to be directly interacting with the stops they have to do it through the shipment all the source code i'm about to show is available to my developer level members in my channel if you want more information about joining go to my channel and click the join button alright so the first thing to look at is actually our stops so i have this abstract class of a stop it has a stop id a status which is going to be in transit arrived or departed and then a sequence so in what order the stops are because this is going to be in a collection so our first method that we have is arrive if we are not in transit meaning we've already arrived or departed then we're going to basically throw an exception if we're good then we'll set our status as arrived and then on depart if we have already departed we'll throw an exception there and then if we're in an inch transit state that means that we haven't arrived yet so we're going to throw under that circumstance otherwise we're going to mark our status as departed so then i have two classes that implement that abstract class i've defined a pickup stop and a delivery stop so all these rules are enforcing the transition of a particular stop but the real problem now is to make sure that these transition happen to the right stops in the right order again meaning that we're doing this to the pickup before we're doing any of these actions to delivery the way we can do that is through the shipment through our aggregate route so the shipment has a constructor that takes our list of stops and this is just going to be in our simple example the pickup and the delivery now we have a few different methods and the most important one to start off with is arrive because this is the thing that's actually going to make sure that we're doing them in the right order so when you pass in the stop id of what stop you want to arrive from what we're doing is we're looking at all the stops to see okay at what particular sequence of that stop we're at so if we're at for example the pickup of our current stop and it's sequence one are there any other stops in that collection that haven't been departed if there are that means that we're not in the current stop that needs to arrive so we'll throw an exception so what this does is that if you try to arrive on say the delivery before the actual pickup is even completed meaning it's in a departed state then we're going to throw an exception this is what's controlling that invariant to make sure that we're doing all the state transitions two stops in the right order so if we get by that and we are in the correct stop that we can do the arrive we're going to call current stop arrive which is what we looked at earlier um to make sure that we um are in the right state for that particular stop and then we change its actual status so back to the shipment we also have the pickup which is doing something very similar so just getting out the actual stop we're checking to see if that stop is actually a pickup if it's not we're going to throw and then we're going to call the depart on the stop which we already looked at as well and then on the deliver method same type of thing as the pickup we're just getting out the stop that was passed in for the stop id making sure it's a delivery and then calling the part on the actual stop so to kind of illustrate how this all works from like a consumer client point of view i have some tests here where i'm creating a new shipment and i'm just giving it my list of stops which are going to be my pickup and my delivery so stop id one's the pickup sequence one stop ids two is the delivery sequence two and my one test is i'm gonna do the arrive the pickup the arrive the delivery in the correct order and then i have this method on the aggregate route the shipment itself called is complete where it just checks to see that all the stops are in a departed state because if they are then that means that our shipment is complete so returning true and then i have a variety of tests that do all this um logic around the invariance for example that you cannot pick up without arriving so i just have my ship up my shipment which i'm trying to do the pickup on stop one but i haven't arrived it yet so that's going to throw our exception the same thing with all those different cases that i was showing in the actual implementation i have a test for every one of them when i'm designing aggregates i'm thinking about invariance and specifically how we handle those invariants that span multiple domain objects like our stop the stop itself can make sure that it goes through the right state transitions but it's our aggregate in our shipment that makes sure that the stops or the collection of stops are done in the correct order my recommendation for designing aggregates is to always be in a consistent and valid state don't expose domain objects that can't enforce all the business rules always be in a consistent and valid state if you found this video helpful please give it a thumbs up if you have any thoughts or questions make sure to leave a comment and as always if you haven't done so already please subscribe for more videos on software architecture and design thanks you
Info
Channel: CodeOpinion
Views: 31,885
Rating: undefined out of 5
Keywords: aggregate design, software architecture, software design, cqrs, event sourcing, design patterns, software architect, soa, microservices, message queues, kafka, event bus, event driven architecture, rabbitmq, distributed transactions, service bus, message queuing, messaging patterns, service oriented architecture, microservice architecture, domain-driven design, enterprise service bus, domain driven design, ddd, aggregates, entities, domain objects, Domain consistency
Id: 64ngP-aUYPc
Channel Id: undefined
Length: 8min 35sec (515 seconds)
Published: Wed Apr 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.