A continuation of this video implementing Domain Services and Domain Events would be nice
@MilanJovanovicTechАй бұрын
I hear you!
@villanuevacarlos14Ай бұрын
Waiting too! Thanks
@rpreviato6 күн бұрын
I've been a developer for a long time, and I've been reluctant to use DDD for a long time because until now I hadn't found anyone who could explain it in such a didactic way as you have been doing. And I confess that I'm enjoying it and maybe I'll make a change for myself. I'll take a look at your course and maybe I'll become your student.
@MilanJovanovicTech5 күн бұрын
Hey, glad to hear that. 😁 If you do end up considering the course, there will be a nice discount next week for Black Friday
@smnb6652Ай бұрын
I don't know why, but these DDD videos are so soothing and interesting to watch. Another great video Milan!
@MilanJovanovicTechАй бұрын
Hey, thanks a lot
@bartlomiejuminskiАй бұрын
More videos on DDD / aggregates / domain / events / messaging please.
@MilanJovanovicTechАй бұрын
Okay, I'll consider how to extend this video with these topics
@LeonardoVargasLАй бұрын
Love DDD and any related video you make about it. A aquired your course and changed the way I think when building models (now domains).
@MilanJovanovicTechАй бұрын
I'm very glad this was helpful! :)
@strandloperАй бұрын
Nice. I would think though that as a meeting requires an organiser that we should provide organiser as a parameter to Create() rather than adding the organiser after creating the meeting.
@MilanJovanovicTechАй бұрын
Actually, that's a great idea! Including it in P2 of the video with other feedback
@antonmartyniukАй бұрын
A video we were waiting for
@MilanJovanovicTechАй бұрын
Finding a decent example takes time
@antonmartyniukАй бұрын
It was worth it @@MilanJovanovicTech
@Paul-uo9sv6 күн бұрын
Awesome thanks Milan
@MilanJovanovicTech6 күн бұрын
Sure thing!
@Christopher-iz4bcАй бұрын
I like rich domain models, but I sometimes feel like I have to wrestle with EF core to get it to recognize the changes added to an aggregate. I feel like sometimes I have to do aggregate.Add(users) and users.add(user) in order to get it to recognize the changes. If not I get a "Database operation expected to affect 1 row(s) but actually affected 0 row(s)." Have you run into this issue? Also, how do you feel about an anemic approach where the domain operations are encapsulated in a static extension class? I think EF also struggles with this sometimes.
@MilanJovanovicTechАй бұрын
I've seen it a couple of times, but this usually happens with Owned types. Right? | Also, how do you feel about an anemic approach where the domain operations are encapsulated in a static extension class? This is only anemic in "name". We still behavior around the domain entity. Although I prefer just having the method in the entity for better cohesion.
@VahidCheshmyАй бұрын
Hi Milan, Thank you for the video. The entity still looks more anemic than a rich domain. You have moved the behaviors into the entity, but the properties are still available to change within the business logic. Also, an entity would have domain events, which are missing in your example.
@MilanJovanovicTechАй бұрын
I commented about the properties in the video. We'll see where I take this in Part 2
@zulimazuliАй бұрын
Good work Milan! I miss specification pattern OR policy pattern for a meetings (instead of meetingPolicyService). Another suggestion would be to use extension methods instead of helper methods. Helpers are sometimes considered a bad practice :)
@MilanJovanovicTechАй бұрын
I don't consider helper methods a bad practice. Why would they be?
@zulimazuliАй бұрын
@@MilanJovanovicTech not bad practice per se, just in relation to extension methods in DDD. Hard to give a short answer. Extension methods are often better than helper methods in DDD because they keep behavior close to domain objects, improve readability, and make code more natural to use. They also promote encapsulation and object-oriented principles, unlike helper methods, which can lead to procedural code and cluttered utility classes
@BlindVirtuosoАй бұрын
The huge problem with your example is that you do not describe the exact invariants which have to be consistent. An aggregate is about enforcing true business invariants by keeping the aggregate consistent.
@arthurprado7527Ай бұрын
Ok, whats ur Github Bro.
@haraheiquedossantos4283Ай бұрын
I kind of agree. I think the first part of the video could be showing all the invariants (business rules) the problem has. And them defining the aggregates. But when he validates the agenda to not exceed the duration of the meeting it is kind of an invariant. So it kind of makes sense be a aggregation of a Meeting.
@MilanJovanovicTechАй бұрын
The idea was to "discover" them as we go, rather than stating them all up front. I wanted to approach it as someone reading the code for the first time and figuring stuff out.
@craigpears2818Ай бұрын
Going more into the "why" is something that's always missing in DDD for me. Having an objective real example of before/after is what I really need to see what value DDD brings, which I appreciate is hard to do in a video. I still struggle to see DDD as an alternative to other approaches rather than being able to understand the domains and problems where it is objectively better.
@MilanJovanovicTechАй бұрын
The real value of DDD is before writing any code. But that's much harder to express in a practical video.
@alvaromp1106Ай бұрын
Great video! A question. In 9:58 you refactored the domain model to include policy logic (Passing the policy object to the Create method), why wouldn't you use an Extension method for that? Something like: meeting.Validate(meetingPolicy) ?
@MilanJovanovicTechАй бұрын
I prefer having it all in one method on the Meeting
@quantum634Ай бұрын
Milan why did you create a static Create function in the domain? Why not have a MeetingFactory ? That will make testing a lot easier. Also why not use AutoMocker for your tests ?
@MilanJovanovicTechАй бұрын
| why did you create a static Create function in the domain? I always preferred a static factory method over a new class for simple creation logic. This is just calling a constructor and a few business rules. A factory would introduce the same code plus an additional dependency. As for AutoMocker, I generally use NSubstitute.
@ghevisartor6005Ай бұрын
why would you need a factory object to create that meeting? Because one day you might need an external dependency? ok so you do this for all your entities just in case?
@JanpillemannOtze-dt5yxАй бұрын
Not a big thing but what’s the advantage of a static create method instead of exposing just one public constructor with the required inputs?
@jandersongoncalves6688Ай бұрын
Awesome
@MilanJovanovicTechАй бұрын
Thanks!
@ciekawki6574Ай бұрын
Are there any general guideliness regarding whether I should use rich or anemic model pattern? So far i've been always using the latter one.
@user-dc9zo7ek5jАй бұрын
You should always go for rich if you want to abide to DDD, because anemic models are an anti pattern. When a model (entity, aggregate, value objects) has only data then it is considered anemic. If it hides its data and exposes behaviour (methods) then it is considered rich. You probably use anemic models because you put the business logic into the services and gave the power to manage your models' state. However this leads to: 1. Unneccessary, or even circular, dependencies between services, 2. Constructor hell, 3. Allowing the whole system to create invalid states with the un-encapsulated models, 4. Fat test setups which makes them fragile. Service logic is only meant to be used when the logic is too complex for one model to contain within, or it has external deps like saving to a database. You can mix them together, with the service only containing support logic such as saving to DB, logging, sending events. While the model can contain the business rules which keeps itself valid. I also do anemic models and fat services. I pay the cost of it with all of the points I mentioned above. I do whatever I find easier to follow and maintain :)
@ciekawki6574Ай бұрын
@@user-dc9zo7ek5j Thank you for your detailed answer!
@MilanJovanovicTechАй бұрын
No guidelines, just preferences. You get something with one approach that you lose with the other (and vice versa).
@ciekawki6574Ай бұрын
@@MilanJovanovicTech Understood. Altough calling anemic model na "anti pattern" got me thinking, that there are only bad things attached to it
@craigpears2818Ай бұрын
@@ciekawki6574 They aren't an anti-pattern, they're much more commonplace than DDD, which is usually chosen in response to a problem. What you and your team is familiar with will be far more important than any objective strengths/weaknesses of each. Rather than trying to predict what's "right" I'd recommend learning how to identify the signs that you've gone down the wrong path. For example if you go with DDD as a default you might grow resentful of going through the effort if your domain is so simle that you are getting no benefits, that emotion/smell is a sign you've gone the wrong way. Going from the opposite direction if you get problems with new releases causing problems because they aren't following business rules that's a sign your simple design needs adapting, DDD could be one of many solutions.
@zocnuteАй бұрын
Great video! How are those participants and agendaItems are being stored in the database?
@MilanJovanovicTechАй бұрын
Separate tables in the database for each, with a FK pointing to the meeting
@joaonadais923Ай бұрын
Great video! Coming from a performance perspective though, the creation of agenda items now went from only needing one pass to get the meeting duration to traversing the list every single time you add an agenda item Would this be of any concern to you?
@MilanJovanovicTechАй бұрын
No concern. We're talking micro-seconds here since the list should fit in memory. What meeting has 1M agenda items? I'd rather quit that company right away 😅
@joaonadais923Ай бұрын
@@MilanJovanovicTech Very true 😄
@norimonstaАй бұрын
If the logic for checking to see if there is conflict with other meetings is put into a domain service. Do we then somehow inject that domain service into the domain model to ensure it's being used when populating the Meeting aggregate?
@quantum634Ай бұрын
Injecting a domain service or general service isn't possible, as a domain isn't something that can be injected. I'd move the check method (which returns a boolean) to the Meeting or UserRepository (persistence layer). Then, in the MeetingService, have a void method like EnsureUserHasNoTimeConflicts that throws an exception if needed (application layer). Call this in the command handler. An even better approach: Forget the MeetingService. Just use the repository method (returning a boolean) in the command handler. Call the result variable isUserConflictFree (or similar) and pass it to AddParticipant. Perform the check there, throwing an exception if needed.
@user-dc9zo7ek5jАй бұрын
Domain models should not have dependency injections for services. They should exist in a world where they only know about their own rules and nothing else. Their mission is to protect themselves and always be valid. I would see if there is an additional sub domain that really handles this logic and is not a lonely service that is being used by multiple models for code reuse. In all cases that relate to cross cutting concerns, services come to the rescue. Here is the flow I imagine: User -> MeetingService -> Checks MeetingChecker -> MeetingService creates Meeting -> Meeting performs its own validations (end date being earlier than start date and so on) -> MeetingService calls the DB or whatever.
@marko5734Ай бұрын
I think it's possible to perform the logic in domain service then send the result as interface instance as argument in the constructor in the application layer. So the agg root can then check this logic and perform some validation
@MilanJovanovicTechАй бұрын
It's absolutely possible to inject a domain service and call its method. This is often called double dispatch. Check these two videos for implementation and tradeoffs: - Double dispatch: kzbin.info/www/bejne/rZrCqKataqyjbLM - Tradeoffs: kzbin.info/www/bejne/m3SaeIB9frdnfdk
@norimonstaАй бұрын
@MilanJovanovicTech Thanks this is exactly what I needed.
@DaminGamerMCАй бұрын
ok but now how do you handle the database insertions if you are changing the mapped properties. for example if i include Participants in a query now ef does not know what to do
@pilotbobaАй бұрын
EF Is super smart and would use the backing fields.
@DaminGamerMCАй бұрын
@@pilotbobahow does it know which backing field it should use
@mymemoryleaksАй бұрын
I would not recommend using domain entity with EF. Map you domain entity to data objects and use them with EF core
@pilotbobaАй бұрын
@@DaminGamerMC Mappings. I'm not sure if the default mappings supports the => because I think that is a method and not a property. But, if he used a property I think it would do it correctly. Or you can specify the field in the mappings.
@MilanJovanovicTechАй бұрын
EF 6/7/8 can handle this just fine, with no additional configuration. Default mapping is Property => _property (backing field)
@timur2887Ай бұрын
Hmm, but now we splitted checks and logic that was in a single class on two: the use case and meeting aggregate. May be we should try to move all logic in domain services?
@MilanJovanovicTechАй бұрын
We'd end up with what we had in the use case (more or less)
@jamescanham6945Ай бұрын
Why did you move the code to the Meeting.Create method instead of a constructor for the Meeting class? :)
@MilanJovanovicTechАй бұрын
I don't like throwing exceptions from constructors, for one. Second thing is this leaves the possibility to use domain events later - again a preference of mine: not having side effects (domain events) in constructors.
@bookfirst-kg3gqАй бұрын
and also automation test books
@MilanJovanovicTechАй бұрын
Book first, got it
@luis1118Ай бұрын
I will just get lost at differentiating domainEntities that i created vs ones that are already tracked and i am just updating; Particularly because I try to call save changes just once in the controller but may have a long dependency of method that pass the entities around
@MilanJovanovicTechАй бұрын
EF is usually pretty good at managing this
@kashminer4897Ай бұрын
First comment from kashmir
@MilanJovanovicTechАй бұрын
Blazing speed
@panagiotischatzoglou7249Ай бұрын
Very nice video! Can you share source code?
@MilanJovanovicTechАй бұрын
In the description (if not then Patreon - can't recall exactly)