Don't be fooled! That's NOT an Aggregate in Domain Driven Design

Ғылым және технология

Do you get frustrated that you have to open multiple files across multiple layers to make what seems to you like a simple change? One of the culprits for this is following structure and templates that apply patterns or concepts to solve problems you might not have. One typical case of this is using aggregate from domain drive design. In this video, I'll give some examples of where an aggregate can make sense and where it's not and adds useless indirection.
🔗 EventStoreDB
eventsto.re/codeopinion
🔔 Subscribe: / @codeopinion
💥 Join this channel to get access to source code & demos!
/ @codeopinion
🔥 Don't have the JOIN button? Support me on Patreon!
/ codeopinion
📝 Blog: codeopinion.com
👋 Twitter: / codeopinion
✨ LinkedIn: / dcomartin
📧 Weekly Updates: mailchi.mp/63c7a0b3ff38/codeo...
0:00 Intro
0:51 Indirection
3:20 Fake Aggregate
6:42 Consistency Boundary
#softwarearchitecture #softwaredesign #domaindrivendesign
'image: Flaticon.com'. This cover has been designed using resources from Flaticon.com

Пікірлер: 92

  • @FlaviusAspra
    @FlaviusAspra Жыл бұрын

    So I cannot over-engineer my solution any more to feel better about myself? 😭 /s

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Unfortunately no! 😂

  • @G0rynych

    @G0rynych

    Жыл бұрын

    Why? Of course you can! Just do not tell anybody ;)

  • @orterves

    @orterves

    Жыл бұрын

    But what if we need all this complexity in the future!?

  • @G0rynych

    @G0rynych

    Жыл бұрын

    @@orterves Then, it's ok. But only if you have fullstack scrum team with separate Oraculus role.

  • @NelCapeTown
    @NelCapeTown Жыл бұрын

    Very good. We only truly master code patterns when we know where, when, and why not to use them. Great reminder of how important that is.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Well said!

  • @einardivision
    @einardivision Жыл бұрын

    Thank you for this video, kinda feel like i've been doing this all over my codebase. I guess it's time to re-evaluate what patterns i actually need to use rather then just using them blindly. I do however believe i should use Aggregate in my codebase since the Validation is quite complicated

  • @evilTano
    @evilTano Жыл бұрын

    I agree, but to be able to use "simple code" you need to REALLY understand WHY you are adding useless complexity. And this is the hardest part. The only way to understand this, is to pratice A LOT of DDD, Clean Architecture etc etc. After I studied a lot of DDD I start to understand when and why don't use Aggregates, CQRS and write a simple Repository Pattern or a Transiction Script. Anyway, my suggestion is: Pratice a LOT of DDD in your code. Write a lot of useless complex code so you can understand where and when use some pattern to resolve your specific problem. By the way, good video as always! :)

  • @MaxPicAxe
    @MaxPicAxe Жыл бұрын

    You're videos are great. It's a very simple case of whether you are validating input (which can simply be done with types) or actually validating the current state of the system.

  • @codewkarim
    @codewkarim Жыл бұрын

    Thankfully, I've watched older videos from you that prevented me from doing this! Honestly, I learn a lot from you.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Great to hear!

  • @PinheiroJaime
    @PinheiroJaime Жыл бұрын

    nice content. you touched upon an interesting topic of over-engineering. it is very common to see applied sophistication just for the sake of showing off.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    I did a video talking about this a bit: kzread.info/dash/bejne/dISH17mOiNynZZs.html

  • @44Bigs
    @44Bigs Жыл бұрын

    I like that validated value object trick! Definitely food for thought.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    It was just a quick example, it can go further in terms of how you implement it (eg, nullable, etc).

  • @johnnyt5514

    @johnnyt5514

    Жыл бұрын

    Be sure to not replace „primitive obsession“ with „value object obsession“. Be conscious of the scope where these objects should be used and assure not to end up with widely shared objects that can’t be changed without harming the whole project. Some extra mapping or a copy of some lines of code might sometimes be better than strong coupling.

  • @KyleSmithNH

    @KyleSmithNH

    Жыл бұрын

    @@johnnyt5514 Indeed, my instinct here was the use of IsSatisfiedBy was the smell, but the validation in this case could be a simple if statement on the setter method. I really like immutable value objects for more complex cases, though.

  • @Sokooo
    @Sokooo Жыл бұрын

    The first thing I see with setting the name directly with a public setter on the product is that I can set it to null or string.Empty. You are not enforcing your invariant now, unless the products name can be null or string.Empty, which I highly doubt. I’d rather still have a setter/edit method which at least checks for nulls or empty strings.

  • @SupraStututututu
    @SupraStututututu Жыл бұрын

    Great video, thank you!

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Glad you liked it!

  • @EldenFiend
    @EldenFiend Жыл бұрын

    I usually prefer to use rich domain models where there's some more interactions/behaviour than the usual, simple, no business logic entities that just get stored in the DB. The order vs product you gave is a great example since they're in the same field. A product is very simple. A catalog, a product page, an order/checkout, that uses some data from the product, is where you'll actually business rules applied. Those business rules probably use a product entity (or multiple) but they're their own thing within the different aggregates.

  • @marna_li
    @marna_li Жыл бұрын

    Thanks for all of your videos! I do think that bombarding the programmer’s mind with stuff do help them start thinking. The problem that newbies have with DDD concepts is that they go directly to code and forgets the point of the patterns. Thinking: “I have to do this” The focus becomes pattern rather than use. You cannot write good comprehendible code just by following patterns, then AI would take your job. If you have logic for a property setter, and you cannot abstract it away in a Value Object, perhaps it is better to manually write the setter, rather than to create this extra method which doesn’t provide any value other value than just the 1-5 lines for validation and setting the field.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    AI will take our job repeating horrible uses of patterns. 😂

  • @marna_li

    @marna_li

    Жыл бұрын

    @@CodeOpinion 😂 Yes. Because they don’t understand context.

  • @ValueLevit

    @ValueLevit

    Жыл бұрын

    Imagine today business doesn't have any business logic bounded to price changing and you implement plain Value Object. Business grows and tens of Price assignments appear in the code. And now business decides to apply new logic to Price assignment. Now you have to apply refactoring to tens of files. Properties are just wrappers above setters and getters. So I don't think that there is a big problem with it. Please correct me if I'm wrong. * Thank you Derek for all you content ❤

  • @derekcomartin4626

    @derekcomartin4626

    Жыл бұрын

    @@ValueLevit Logic would be in the VO. If you needed something outside of that to the assignment, then refactor towards what was illustrated on the second half of the video. Don't stare with more indirection just "in-case". Beyond all that, if you have 100's of assignments for price, i'd be looking at that first as to why you have so many assignments. That's a smell.

  • @Ogix7
    @Ogix7 Жыл бұрын

    I usually check string lengths in my Create / Set methods. Do you think in that case it's ok to have set methods or string lengths should not be checked at all and delegate those checks to database?

  • @haraheiquedossantos4283

    @haraheiquedossantos4283

    Жыл бұрын

    Good question

  • @acegame1452
    @acegame1452 Жыл бұрын

    Hello Derek! I am building an event driven application and i am currently developing a module that manages products in an ecommerce. The idea is that for some things i do not need an aggregate like u explained however in that case how would i publish the corresponding events if not inside an aggregate. For example ProductAddedEvent? Thanks in advance

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    There's no magic, just publish the event to whatever you're using to do so. If its simply just CRUD, then do the state change and publish the event. Be aware of if you need to reliably publish the event and if you want a fallback or use the outbox pattern. Check out kzread.info/dash/bejne/p2yasdCxcduxeM4.html

  • @mohamadyousef163
    @mohamadyousef163 Жыл бұрын

    This is a great example about DDD aggregates and their purpose I also watched your time zones video and how to schedule an event in the future I found it a little bit vague could you make a more detailed video with an example Thanks

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Regarding the future events, check out this one that shows more code if you want an example: kzread.info/dash/bejne/rIub08xrf6ixiJs.html

  • @nicolaibaaring1353
    @nicolaibaaring1353 Жыл бұрын

    Do you have an example of when an Aggregate Root becomes to big and how to deal with it? I have an example where there is one single consistency boundary, but the objects inside the AR have two different "directions" - thus not related. Example: A conference with speakers and participants. Both are bound by the rules of the conference, but the participants are not guarded by speakers and vice versa. But they are each guarded by themselves and the conference boundary. Should this be split to different aggregates or kept and allow the AR to grow? If split, how to maintain the AR as a boundary for both? Thanks

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    I don't have a video explicitly about that, but this video might help: kzread.info/dash/bejne/aGiiybJmkbi_gMY.html

  • @thedacian123
    @thedacian123 Жыл бұрын

    How domain events should be raised within an aggregate, and which will be the subscribers(entities other aggregates,maybe a value object)Thanks

  • @erfan9420
    @erfan9420 Жыл бұрын

    Good concept, I have a question here: I agree with you about the "Name" property because it's string and can be stored in database but when we have ProductPrice type and we remove the setter method, We should change the "Price" property type to ProductPrice which is not supported by ORMs like EF Core. So we need the setter method again because our data model cannot be stored in database with property with value type "ProductPrice".

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Check out this video: kzread.info/dash/bejne/eaiLuKmDoJOcYaQ.html

  • @brandon995
    @brandon995 Жыл бұрын

    In your Product example, what happens whenever you add logic that should enforce the consistency boundary and/or you'd like to publish one or two domain events from the product? Would you keep the existing public setters or convert the entire object to use private setters? Seems like you would..

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    If I truly needed logic around setting the price or needed to publish an event, I would remove the public setters, add methods to the product itself that reflected the actual behavior. Eg, product.IncreasePrice(val) which would probably publish a PriceIncreased event. Not a SetPrice().

  • @IAMAliJan
    @IAMAliJan Жыл бұрын

    explained in a good way.

  • @54114142
    @54114142 Жыл бұрын

    What would you do when you have both? Some setter methods with necessary consistency validation and a lot of stupid properties? I kinda hate to mix both concepts so I sometimes end up with setter methods that are not really necessary. :/

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Then have methods where needed and just public properties where its not needed.

  • @heikenem
    @heikenem Жыл бұрын

    Great video. But one question. For validate the price you replaced the logic inside the "domain object" for a Value Object and great strategy. But what if you dont use a value object to make those validations of a specific property(s), what is the strategy? make the validation inside the transactions scripts using for example some kind of validation lib/framework knowing that you have the chance that your "domain model" (anemic model) can be in invalid state?

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Put the validation in the trx script if you have an anemic domain model, you don't have any other options. But this is where you start losing consistency as you could eventually have somewhere else that sets the price and won't do the validation. Hence the domain model or value objects forcing the consumer to provide valid values.

  • @Galakyllz
    @Galakyllz Жыл бұрын

    One of my coworkers follows DDDD (Dogmatic Domain Driven Design) where higher abstractions create an instance of a model (empty constructor, filled with null/default values) just to call a method on that model (passing in the repository object) to get back a collection of that model... because "it's domain driven design". There's no reasoning with this man. Believe me, I have tried.

  • @tomaszpajak9227
    @tomaszpajak9227 Жыл бұрын

    Problem with using struct like this for validation is that we can pass invalid structure to the aggregate. For example in case structure got it's default value. ProductPrice productPrice = default; Also there is an error in your code since domain exception will be thrown when product price equals 0 but the message says that price cannot be negative. Anyway I'm still a regular on your channel, great work.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    You could throw in the implicit conversion or elsewhere that you return the underlying value. It doesn't just have to be on the ctor alone.

  • @pickle1987

    @pickle1987

    Жыл бұрын

    to create your value object you can use Records instead of Struct, the problem that I found with valueobjects is mapping them to the database model, so I prefer the specification pattern

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    @@pickle1987 Which a reason I don't find trying to force a domain model and a data model to be the same thing because of mapping concerns. kzread.info/dash/bejne/eaiLuKmDoJOcYaQ.html

  • @G0rynych

    @G0rynych

    Жыл бұрын

    @@pickle1987 VOs are simply mapping to its values :) There should not be a problem at all.

  • @Hlautameki
    @Hlautameki Жыл бұрын

    Hi, what color scheme are you using?

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Pretty sure it's the "Rider Dark"?

  • @gwholdom
    @gwholdom Жыл бұрын

    Excellent.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Many thanks!

  • @danielpotzinger7970
    @danielpotzinger7970 Жыл бұрын

    The UML diagram used wrong syntax. The diamond indicated "aggregates" and need to be on the basket

  • @cdarrigo
    @cdarrigo Жыл бұрын

    So if the properties become publically settable, then we don't use the value objects to set them. We can directly set item.price = request.price. Where do you perform the validation to ensure price isn't a negative value?

  • @G0rynych

    @G0rynych

    Жыл бұрын

    If "the properties become publically settable" then we do not have objects at all :))

  • @cdarrigo

    @cdarrigo

    Жыл бұрын

    @@G0rynych I don't understand that

  • @G0rynych

    @G0rynych

    Жыл бұрын

    @@cdarrigo Discussion about ValueObjects (Objects!) and Aggregates is valid only if we are talking about OOP. With publically settable properties we do not have OOP, so all the discussion loose its sense.

  • @nimmneun
    @nimmneun Жыл бұрын

    It's hard enough to come up with simple solid solutions, so I really wanna throw something at them, when I review someone's PR that's adding extra complexity without benefit/need ... and yes sometimes I really wanna slap myself for doing it as well 😅

  • @kubastepaniak8184
    @kubastepaniak8184 Жыл бұрын

    How would you apply this 'flexible' approach to root + members situation? Let's make up an example - there's an aggregate root called Company with a list of members of type CompanyDepartment. Company could have a method ExtendServiceOffer(Service) which would iterate through Departments checking if any of them have the capability of providing such Service and if not - add new (it might make no sense in real world, it's just for the sake of an example). At the same time there might be a need to update some simple properties of Department like name. Changing name does not break any consistency of an aggregate so theoretically it could be done with public setter, but there's this rule saying that you should not access aggregate member directly, only via aggregate root. This is a fairly simple example, but I can imagine easily having more levels of aggregate members (DepratmentWorkers, WorkerSkills), which for sure will have some simple properties.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    You don't need one model to rule them all. Meaning, you don't need to use an aggregate for the entire thing. This is the problem when we try to conflate a data model with also being an entity apart of an aggregate. If you want to for code consistency, then sure, however I don't buy the "there this rule saying...". Be pragmatic. Check out this video, might give some other details that might be helpful: kzread.info/dash/bejne/eaiLuKmDoJOcYaQ.html

  • @kubastepaniak8184

    @kubastepaniak8184

    Жыл бұрын

    ​@@CodeOpinion "You don't need one model to rule them all" - you saing that there should be maybe a separate model (in a separate bounded context I suppose) which would let us directly handle simple CRUD operations and an aggregate cointaining the minimum required to keep the consistency, right?

  • @mhDuke
    @mhDuke Жыл бұрын

    in the fake aggregate, you suggested to use value object ProductPrice, and also suggested to git rid of the SetPrice(), and set the Price property directly outside the "fake aggregate", but since the Price is now an object, it could be null, what if we cant have a null price? that said, i get the general idea.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    It's a struct. It can't be null

  • @mhDuke

    @mhDuke

    Жыл бұрын

    @@CodeOpinion Right. I didnt use structs for value objects because I mapped some of them as Owned Entities instead of using a value converter for some reason back then, so I dropped using structs for value objects all together, however, structs support for Owned entities should come with ef core 8. That said, I think you were explaining the theory. when it gets to implementation, we will always have some limitations depending on what language/frameworks we use.

  • @lost-prototype
    @lost-prototype Жыл бұрын

    God your videos are so good.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Glad you like them!

  • @andrewthompson9714
    @andrewthompson9714 Жыл бұрын

    It's awesome to see the subject of primitive obsession tackled. Every codebase I see is a plague of validation logic and an exception throwing competition. If you declare a method to take "int" then there is no WAY you should be throwing an exception when I give you the wrong kind of "int"

  • @patchr6288

    @patchr6288

    Жыл бұрын

    > If you declare a method to take "int" then there is no WAY you should be throwing an exception when I give you the wrong kind of "int" That is a really interesting and meaningful way to put this problem.

  • @gerarduab9960
    @gerarduab9960 Жыл бұрын

    So the aproach it's to create plain models without any kind of logic? becouse all the video examples contains agreggate clausules.

  • @phugia963
    @phugia963 Жыл бұрын

    Great video, can I ask where I can find the source code for reference? Thanks

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    github.com/dotnet-architecture/eShopOnContainers

  • @vocod3
    @vocod3 Жыл бұрын

    thank you for work keys and crack

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Not sure what that means, but sure!

  • @EmperorFool
    @EmperorFool Жыл бұрын

    While I agree the Product setters were unnecessary, Product isn't an aggregate even though it implements IAggregateRoot. This would have been better if it showed a bad example of an aggregate, especially when the video title is "That's NOT an Aggregate". You're right, it's not, and thus it's not an example of inappropriately using an aggregate.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    That's the point of the video, that what people think are aggregates, usually aren't. Not entirely sure what a "bad aggregate" would be beyond that? A bloated one?

  • @EmperorFool

    @EmperorFool

    Жыл бұрын

    @@CodeOpinion I meant that the Product class you found online didn't look like it was acting like an aggregate. Yes, it implemented an aggregate interface, but it had no methods that made me think the author actually intended it to serve as one. I guess I was expecting to see an example class that acted like an aggregate but shouldn't be. No worries.

  • @rtl6832
    @rtl6832 Жыл бұрын

    I find your topics interesting, but would you consider doing some KZread shorts or TLDR versions of your videos? It's a lot of unnecessary explanation for the more experienced developers.

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Yes, I might

  • @codewkarim
    @codewkarim Жыл бұрын

    "All problems in computer science can be solved by another level of indirection" except too many levels of indirections >_

  • @Fikusiklol
    @Fikusiklol8 ай бұрын

    Cant really agree with your statement. What you've shown is a simply DDD versus CRUD comparison and where CRUD can actually be easier to implement and maintain. Aggregates are not only about transactional/consistency boundary.

  • @lucasterable
    @lucasterable9 ай бұрын

    // For EF ;)

  • @creepychris420
    @creepychris420 Жыл бұрын

    lol innit bro

  • @mwatkins0590
    @mwatkins0590 Жыл бұрын

    this video confuses me a bit. It seems you are implying that using getters/setters is inherently an aggregate pattern. I thought an aggregate is something like a data model which does not have direct dependencies on other data models which are outside of its boundary, but instead just a reference to them. like in your example here order would be an aggregate if it had a property CustomerId int instead of a property customer Customer. another thing that confuses me is with the Value object recommendation here. You are saying you do the validation in the value object, but then this would require all things to construct that value object before passing it to the setter. It does not seem to me this is more convenient or effecient than what was already there. Lets consider an alternative, what if each property had a setter that took an int as a parameter, and the setter itself converted the int to the value object. It seems this code would likely be more convenient than your suggested change because generally you would be likely to extract an int from say a REST request moreso than a PriceProperty class, and this would significantly uglify your json messages to and from your front end. But then in these setters im proposing, you would have that line of code which does the value property conversion which includes the validation and it sort of essentially works the same way as what was originally there, so what was the point in making any of these changes anyway? Do you really need to dig into a class file to read what the setter and getter is doing anyway? isn't that the point of encapsulation? just give me the value dont worry about the implementation this class takes care of those details you dont need to know these details to go on with the rest of your necessary changes? Which brings me to my last question, if you set up the classes as you call them as "data models" (to me a model can have getters and setters with private fields but it seems the terminology is different between us), and you directly set and access teh public properties on these models in other classes - what if you want to add some extra logic later on down the line, say a year or 10 later in your company, which would make you want to instead encapsulate that logic inside a setter or getter. Wouldn't that be annoying to go through every usage of that field and change it to use a setter/getter and to redeclare the field as private? When your code can autogenerate getters and setters for you, and when the point and usage of getters and setters is so not complicated much in the first place, are you really gaining much in terms of reducing complexity by axing getters and setters?

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    The common perception of an aggregate is a data model with little behavior. Typically those "behaviors" being trivial setters, which it is not. The purpose of an aggregate is to be a consistency boundary. This video might explain more: kzread.info/dash/bejne/aGiiybJmkbi_gMY.html

  • @mwatkins0590

    @mwatkins0590

    Жыл бұрын

    @@CodeOpinion Oh I see. In the code example they call it an aggregate, but its not actually an aggregate, its a Fake Aggregate (as your index says).

  • @ashrasmun1
    @ashrasmun1 Жыл бұрын

    this is OOP absurdity at it's peak

  • @MechanicalMooCow
    @MechanicalMooCow Жыл бұрын

    Sorry, what? These examples are C#, this dude has a property that is public, explicitly sets the set private, but then creates a method that essentially sets it public... May as wellnput any of that methodology logic into the properties setter...

  • @CodeOpinion

    @CodeOpinion

    Жыл бұрын

    Ah, not sure you had the volume on?

Келесі