A continuation of this video implementing Domain Services and Domain Events would be nice
@MilanJovanovicTech21 күн бұрын
I hear you!
@villanuevacarlos1420 күн бұрын
Waiting too! Thanks
@smnb665221 күн бұрын
I don't know why, but these DDD videos are so soothing and interesting to watch. Another great video Milan!
@MilanJovanovicTech21 күн бұрын
Hey, thanks a lot
@bartlomiejuminski21 күн бұрын
More videos on DDD / aggregates / domain / events / messaging please.
@MilanJovanovicTech21 күн бұрын
Okay, I'll consider how to extend this video with these topics
@strandloper21 күн бұрын
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.
@MilanJovanovicTech21 күн бұрын
Actually, that's a great idea! Including it in P2 of the video with other feedback
@Christopher-iz4bc21 күн бұрын
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.
@MilanJovanovicTech21 күн бұрын
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.
@LeonardoVargasL18 күн бұрын
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).
@MilanJovanovicTech17 күн бұрын
I'm very glad this was helpful! :)
@antonmartyniuk21 күн бұрын
A video we were waiting for
@MilanJovanovicTech21 күн бұрын
Finding a decent example takes time
@antonmartyniuk21 күн бұрын
It was worth it @@MilanJovanovicTech
@VahidCheshmy14 күн бұрын
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.
@MilanJovanovicTech14 күн бұрын
I commented about the properties in the video. We'll see where I take this in Part 2
@alvaromp110617 күн бұрын
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) ?
@MilanJovanovicTech16 күн бұрын
I prefer having it all in one method on the Meeting
@craigpears281812 күн бұрын
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.
@MilanJovanovicTech11 күн бұрын
The real value of DDD is before writing any code. But that's much harder to express in a practical video.
@zulimazuli19 күн бұрын
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 :)
@MilanJovanovicTech19 күн бұрын
I don't consider helper methods a bad practice. Why would they be?
@zulimazuli19 күн бұрын
@@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
@egetuncoz573820 күн бұрын
Great video! How are those participants and agendaItems are being stored in the database?
@MilanJovanovicTech20 күн бұрын
Separate tables in the database for each, with a FK pointing to the meeting
@quantum63421 күн бұрын
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 ?
@MilanJovanovicTech21 күн бұрын
| 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.
@ghevisartor600518 күн бұрын
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-dt5yx12 күн бұрын
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?
@ciekawki657421 күн бұрын
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-dc9zo7ek5j21 күн бұрын
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 :)
@ciekawki657421 күн бұрын
@@user-dc9zo7ek5j Thank you for your detailed answer!
@MilanJovanovicTech21 күн бұрын
No guidelines, just preferences. You get something with one approach that you lose with the other (and vice versa).
@ciekawki657421 күн бұрын
@@MilanJovanovicTech Understood. Altough calling anemic model na "anti pattern" got me thinking, that there are only bad things attached to it
@craigpears281812 күн бұрын
@@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.
@joaonadais92318 күн бұрын
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?
@MilanJovanovicTech17 күн бұрын
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 😅
@joaonadais92317 күн бұрын
@@MilanJovanovicTech Very true 😄
@norimonsta21 күн бұрын
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?
@quantum63421 күн бұрын
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-dc9zo7ek5j21 күн бұрын
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.
@marko573421 күн бұрын
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
@MilanJovanovicTech21 күн бұрын
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
@norimonsta20 күн бұрын
@MilanJovanovicTech Thanks this is exactly what I needed.
@BlindVirtuoso21 күн бұрын
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.
@arthurprado752721 күн бұрын
Ok, whats ur Github Bro.
@haraheiquedossantos428321 күн бұрын
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.
@MilanJovanovicTech21 күн бұрын
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.
@timur288716 күн бұрын
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?
@MilanJovanovicTech16 күн бұрын
We'd end up with what we had in the use case (more or less)
@jamescanham694520 күн бұрын
Why did you move the code to the Meeting.Create method instead of a constructor for the Meeting class? :)
@MilanJovanovicTech20 күн бұрын
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.
@jandersongoncalves668815 күн бұрын
Awesome
@MilanJovanovicTech15 күн бұрын
Thanks!
@DaminGamerMC21 күн бұрын
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
@pilotboba21 күн бұрын
EF Is super smart and would use the backing fields.
@DaminGamerMC21 күн бұрын
@@pilotbobahow does it know which backing field it should use
@mymemoryleaks21 күн бұрын
I would not recommend using domain entity with EF. Map you domain entity to data objects and use them with EF core
@pilotboba21 күн бұрын
@@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.
@MilanJovanovicTech21 күн бұрын
EF 6/7/8 can handle this just fine, with no additional configuration. Default mapping is Property => _property (backing field)
@luis111820 күн бұрын
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
@MilanJovanovicTech20 күн бұрын
EF is usually pretty good at managing this
@panagiotischatzoglou724918 күн бұрын
Very nice video! Can you share source code?
@MilanJovanovicTech18 күн бұрын
In the description (if not then Patreon - can't recall exactly)