A Natural Path to Domain-Driven Design (evolve your code)

  Рет қаралды 15,765

Milan Jovanović

Milan Jovanović

Күн бұрын

Пікірлер: 81
@fernandodona5197
@fernandodona5197 Ай бұрын
A continuation of this video implementing Domain Services and Domain Events would be nice
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
I hear you!
@villanuevacarlos14
@villanuevacarlos14 Ай бұрын
Waiting too! Thanks
@rpreviato
@rpreviato 2 күн бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech Күн бұрын
Hey, glad to hear that. 😁 If you do end up considering the course, there will be a nice discount next week for Black Friday
@bartlomiejuminski
@bartlomiejuminski Ай бұрын
More videos on DDD / aggregates / domain / events / messaging please.
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Okay, I'll consider how to extend this video with these topics
@smnb6652
@smnb6652 Ай бұрын
I don't know why, but these DDD videos are so soothing and interesting to watch. Another great video Milan!
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Hey, thanks a lot
@strandloper
@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
@MilanJovanovicTech Ай бұрын
Actually, that's a great idea! Including it in P2 of the video with other feedback
@LeonardoVargasL
@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
@MilanJovanovicTech Ай бұрын
I'm very glad this was helpful! :)
@Christopher-iz4bc
@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
@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
@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
@MilanJovanovicTech Ай бұрын
I commented about the properties in the video. We'll see where I take this in Part 2
@Paul-uo9sv
@Paul-uo9sv 3 күн бұрын
Awesome thanks Milan
@MilanJovanovicTech
@MilanJovanovicTech 2 күн бұрын
Sure thing!
@antonmartyniuk
@antonmartyniuk Ай бұрын
A video we were waiting for
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Finding a decent example takes time
@antonmartyniuk
@antonmartyniuk Ай бұрын
It was worth it ​@@MilanJovanovicTech
@craigpears2818
@craigpears2818 29 күн бұрын
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
@MilanJovanovicTech 29 күн бұрын
The real value of DDD is before writing any code. But that's much harder to express in a practical video.
@zulimazuli
@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
@MilanJovanovicTech Ай бұрын
I don't consider helper methods a bad practice. Why would they be?
@zulimazuli
@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
@alvaromp1106
@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
@MilanJovanovicTech Ай бұрын
I prefer having it all in one method on the Meeting
@ciekawki6574
@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
@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
@ciekawki6574 Ай бұрын
@@user-dc9zo7ek5j Thank you for your detailed answer!
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
No guidelines, just preferences. You get something with one approach that you lose with the other (and vice versa).
@ciekawki6574
@ciekawki6574 Ай бұрын
@@MilanJovanovicTech Understood. Altough calling anemic model na "anti pattern" got me thinking, that there are only bad things attached to it
@craigpears2818
@craigpears2818 29 күн бұрын
​@@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
@zocnute Ай бұрын
Great video! How are those participants and agendaItems are being stored in the database?
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Separate tables in the database for each, with a FK pointing to the meeting
@quantum634
@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
@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
@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
@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?
@norimonsta
@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
@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
@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
@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
@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
@norimonsta Ай бұрын
@MilanJovanovicTech Thanks this is exactly what I needed.
@timur2887
@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
@MilanJovanovicTech Ай бұрын
We'd end up with what we had in the use case (more or less)
@joaonadais923
@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
@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
@joaonadais923 Ай бұрын
@@MilanJovanovicTech Very true 😄
@DaminGamerMC
@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
@pilotboba Ай бұрын
EF Is super smart and would use the backing fields.
@DaminGamerMC
@DaminGamerMC Ай бұрын
@@pilotbobahow does it know which backing field it should use
@mymemoryleaks
@mymemoryleaks Ай бұрын
I would not recommend using domain entity with EF. Map you domain entity to data objects and use them with EF core
@pilotboba
@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
@MilanJovanovicTech Ай бұрын
EF 6/7/8 can handle this just fine, with no additional configuration. Default mapping is Property => _property (backing field)
@BlindVirtuoso
@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
@arthurprado7527 Ай бұрын
Ok, whats ur Github Bro.
@haraheiquedossantos4283
@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
@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.
@jamescanham6945
@jamescanham6945 Ай бұрын
Why did you move the code to the Meeting.Create method instead of a constructor for the Meeting class? :)
@MilanJovanovicTech
@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.
@luis1118
@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
@MilanJovanovicTech Ай бұрын
EF is usually pretty good at managing this
@jandersongoncalves6688
@jandersongoncalves6688 Ай бұрын
Awesome
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Thanks!
@bookfirst-kg3gq
@bookfirst-kg3gq Ай бұрын
and also automation test books
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Book first, got it
@panagiotischatzoglou7249
@panagiotischatzoglou7249 Ай бұрын
Very nice video! Can you share source code?
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
In the description (if not then Patreon - can't recall exactly)
@kashminer4897
@kashminer4897 Ай бұрын
First comment from kashmir
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Blazing speed
@bookfirst-kg3gq
@bookfirst-kg3gq Ай бұрын
I would suggest to read DDD books first!
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Ok
Completely Get Rid of Exceptions Using This Technique
19:24
Milan Jovanović
Рет қаралды 26 М.
Modeling a Domain With Domain-Driven Design From Scratch | DDD
19:10
Milan Jovanović
Рет қаралды 92 М.
They Chose Kindness Over Abuse in Their Team #shorts
00:20
I migliori trucchetti di Fabiosa
Рет қаралды 12 МЛН
What type of pedestrian are you?😄 #tiktok #elsarca
00:28
Elsa Arca
Рет қаралды 32 МЛН
Увеличили моцареллу для @Lorenzo.bagnati
00:48
Кушать Хочу
Рет қаралды 8 МЛН
Domain-Driven Refactoring - Jimmy Bogard - NDC London 2022
1:00:03
NDC Conferences
Рет қаралды 46 М.
Sqlite Is Getting So Good
28:52
ThePrimeTime
Рет қаралды 204 М.
Domain-Driven Design: The Last Explanation You'll Ever Need
21:05
Software Developer Diaries
Рет қаралды 10 М.
The Magic Of ARM w/ Casey Muratori
1:25:01
ThePrimeTime
Рет қаралды 151 М.
Building Change Data Capture (CDC) in .NET with Debezium + RabbitMQ
21:39
Migrating From Docker Compose to .NET Aspire (my experience)
17:01
Milan Jovanović
Рет қаралды 13 М.
Completely Get Rid of Null Using This Technique
25:28
Milan Jovanović
Рет қаралды 20 М.
They Chose Kindness Over Abuse in Their Team #shorts
00:20
I migliori trucchetti di Fabiosa
Рет қаралды 12 МЛН