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 Жыл бұрын
Well said!
@FlaviusAspra Жыл бұрын
So I cannot over-engineer my solution any more to feel better about myself? 😭 /s
@CodeOpinion Жыл бұрын
Unfortunately no! 😂
@G0rynych Жыл бұрын
Why? Of course you can! Just do not tell anybody ;)
@orterves Жыл бұрын
But what if we need all this complexity in the future!?
@G0rynych Жыл бұрын
@@orterves Then, it's ok. But only if you have fullstack scrum team with separate Oraculus role.
@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! :)
@codewkarim Жыл бұрын
Thankfully, I've watched older videos from you that prevented me from doing this! Honestly, I learn a lot from you.
@CodeOpinion Жыл бұрын
Great to hear!
@44Bigs Жыл бұрын
I like that validated value object trick! Definitely food for thought.
@CodeOpinion Жыл бұрын
It was just a quick example, it can go further in terms of how you implement it (eg, nullable, etc).
@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 Жыл бұрын
@@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.
@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 Жыл бұрын
AI will take our job repeating horrible uses of patterns. 😂
@marna_li Жыл бұрын
@@CodeOpinion 😂 Yes. Because they don’t understand context.
@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 Жыл бұрын
@@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.
@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
@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.
@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.
@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.
@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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@@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. kzfaq.info/get/bejne/fdqHiap71ZaaYXU.html
@G0rynych Жыл бұрын
@@pickle1987 VOs are simply mapping to its values :) There should not be a problem at all.
@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.
@danielpotzinger7970 Жыл бұрын
The UML diagram used wrong syntax. The diamond indicated "aggregates" and need to be on the basket
@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 Жыл бұрын
I did a video talking about this a bit: kzfaq.info/get/bejne/eLaDqLqGvd-lZWw.html
@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 Жыл бұрын
I don't have a video explicitly about that, but this video might help: kzfaq.info/get/bejne/bJqemrNexru9gJc.html
@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 Жыл бұрын
Regarding the future events, check out this one that shows more code if you want an example: kzfaq.info/get/bejne/sL2XpM1jtKuviGw.html
@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 😅
@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 Жыл бұрын
> 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.
@SupraStututututu Жыл бұрын
Great video, thank you!
@CodeOpinion Жыл бұрын
Glad you liked it!
@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 Жыл бұрын
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 kzfaq.info/get/bejne/q56WgtGppt6veJ8.html
@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 Жыл бұрын
Check out this video: kzfaq.info/get/bejne/fdqHiap71ZaaYXU.html
@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 Жыл бұрын
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.
@thedacian123 Жыл бұрын
How domain events should be raised within an aggregate, and which will be the subscribers(entities other aggregates,maybe a value object)Thanks
@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 Жыл бұрын
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 Жыл бұрын
@@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.
@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 Жыл бұрын
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 Жыл бұрын
explained in a good way.
@gerarduab9960 Жыл бұрын
So the aproach it's to create plain models without any kind of logic? becouse all the video examples contains agreggate clausules.
@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 Жыл бұрын
It's a struct. It can't be null
@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.
@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 Жыл бұрын
Good question
@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 Жыл бұрын
If "the properties become publically settable" then we do not have objects at all :))
@cdarrigo Жыл бұрын
@@G0rynych I don't understand that
@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.
@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 Жыл бұрын
Then have methods where needed and just public properties where its not needed.
@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 Жыл бұрын
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: kzfaq.info/get/bejne/fdqHiap71ZaaYXU.html
@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?
@rtl6832 Жыл бұрын
I find your topics interesting, but would you consider doing some KZfaq shorts or TLDR versions of your videos? It's a lot of unnecessary explanation for the more experienced developers.
@CodeOpinion Жыл бұрын
Yes, I might
@Fikusiklol9 ай бұрын
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.
@Hlautameki Жыл бұрын
Hi, what color scheme are you using?
@CodeOpinion Жыл бұрын
Pretty sure it's the "Rider Dark"?
@gwholdom Жыл бұрын
Excellent.
@CodeOpinion Жыл бұрын
Many thanks!
@phugia963 Жыл бұрын
Great video, can I ask where I can find the source code for reference? Thanks
@CodeOpinion Жыл бұрын
github.com/dotnet-architecture/eShopOnContainers
@codewkarim Жыл бұрын
"All problems in computer science can be solved by another level of indirection" except too many levels of indirections >_
@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 Жыл бұрын
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: kzfaq.info/get/bejne/bJqemrNexru9gJc.html
@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).
@lost-prototype Жыл бұрын
God your videos are so good.
@CodeOpinion Жыл бұрын
Glad you like them!
@vocod3 Жыл бұрын
thank you for work keys and crack
@CodeOpinion Жыл бұрын
Not sure what that means, but sure!
@lucasterable10 ай бұрын
// For EF ;)
@creepychris420 Жыл бұрын
lol innit bro
@ashrasmun1 Жыл бұрын
this is OOP absurdity at it's peak
@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...