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

  Рет қаралды 14,619

Milan Jovanović

Milan Jovanović

Күн бұрын

Пікірлер: 77
@fernandodona5197
@fernandodona5197 21 күн бұрын
A continuation of this video implementing Domain Services and Domain Events would be nice
@MilanJovanovicTech
@MilanJovanovicTech 21 күн бұрын
I hear you!
@villanuevacarlos14
@villanuevacarlos14 20 күн бұрын
Waiting too! Thanks
@smnb6652
@smnb6652 21 күн бұрын
I don't know why, but these DDD videos are so soothing and interesting to watch. Another great video Milan!
@MilanJovanovicTech
@MilanJovanovicTech 21 күн бұрын
Hey, thanks a lot
@bartlomiejuminski
@bartlomiejuminski 21 күн бұрын
More videos on DDD / aggregates / domain / events / messaging please.
@MilanJovanovicTech
@MilanJovanovicTech 21 күн бұрын
Okay, I'll consider how to extend this video with these topics
@strandloper
@strandloper 21 күн бұрын
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 21 күн бұрын
Actually, that's a great idea! Including it in P2 of the video with other feedback
@Christopher-iz4bc
@Christopher-iz4bc 21 күн бұрын
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 21 күн бұрын
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.
@LeonardoVargasL
@LeonardoVargasL 18 күн бұрын
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 17 күн бұрын
I'm very glad this was helpful! :)
@antonmartyniuk
@antonmartyniuk 21 күн бұрын
A video we were waiting for
@MilanJovanovicTech
@MilanJovanovicTech 21 күн бұрын
Finding a decent example takes time
@antonmartyniuk
@antonmartyniuk 21 күн бұрын
It was worth it ​@@MilanJovanovicTech
@VahidCheshmy
@VahidCheshmy 14 күн бұрын
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 14 күн бұрын
I commented about the properties in the video. We'll see where I take this in Part 2
@alvaromp1106
@alvaromp1106 17 күн бұрын
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 16 күн бұрын
I prefer having it all in one method on the Meeting
@craigpears2818
@craigpears2818 12 күн бұрын
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 11 күн бұрын
The real value of DDD is before writing any code. But that's much harder to express in a practical video.
@zulimazuli
@zulimazuli 19 күн бұрын
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 19 күн бұрын
I don't consider helper methods a bad practice. Why would they be?
@zulimazuli
@zulimazuli 19 күн бұрын
@@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
@egetuncoz5738
@egetuncoz5738 20 күн бұрын
Great video! How are those participants and agendaItems are being stored in the database?
@MilanJovanovicTech
@MilanJovanovicTech 20 күн бұрын
Separate tables in the database for each, with a FK pointing to the meeting
@quantum634
@quantum634 21 күн бұрын
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 21 күн бұрын
| 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 18 күн бұрын
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 12 күн бұрын
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?
@ciekawki6574
@ciekawki6574 21 күн бұрын
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 21 күн бұрын
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 21 күн бұрын
@@user-dc9zo7ek5j Thank you for your detailed answer!
@MilanJovanovicTech
@MilanJovanovicTech 21 күн бұрын
No guidelines, just preferences. You get something with one approach that you lose with the other (and vice versa).
@ciekawki6574
@ciekawki6574 21 күн бұрын
@@MilanJovanovicTech Understood. Altough calling anemic model na "anti pattern" got me thinking, that there are only bad things attached to it
@craigpears2818
@craigpears2818 12 күн бұрын
​@@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.
@joaonadais923
@joaonadais923 18 күн бұрын
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 17 күн бұрын
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 17 күн бұрын
@@MilanJovanovicTech Very true 😄
@norimonsta
@norimonsta 21 күн бұрын
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 21 күн бұрын
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 21 күн бұрын
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 21 күн бұрын
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 21 күн бұрын
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 20 күн бұрын
@MilanJovanovicTech Thanks this is exactly what I needed.
@BlindVirtuoso
@BlindVirtuoso 21 күн бұрын
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 21 күн бұрын
Ok, whats ur Github Bro.
@haraheiquedossantos4283
@haraheiquedossantos4283 21 күн бұрын
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 21 күн бұрын
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.
@timur2887
@timur2887 16 күн бұрын
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 16 күн бұрын
We'd end up with what we had in the use case (more or less)
@jamescanham6945
@jamescanham6945 20 күн бұрын
Why did you move the code to the Meeting.Create method instead of a constructor for the Meeting class? :)
@MilanJovanovicTech
@MilanJovanovicTech 20 күн бұрын
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.
@jandersongoncalves6688
@jandersongoncalves6688 15 күн бұрын
Awesome
@MilanJovanovicTech
@MilanJovanovicTech 15 күн бұрын
Thanks!
@DaminGamerMC
@DaminGamerMC 21 күн бұрын
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 21 күн бұрын
EF Is super smart and would use the backing fields.
@DaminGamerMC
@DaminGamerMC 21 күн бұрын
@@pilotbobahow does it know which backing field it should use
@mymemoryleaks
@mymemoryleaks 21 күн бұрын
I would not recommend using domain entity with EF. Map you domain entity to data objects and use them with EF core
@pilotboba
@pilotboba 21 күн бұрын
@@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 21 күн бұрын
EF 6/7/8 can handle this just fine, with no additional configuration. Default mapping is Property => _property (backing field)
@luis1118
@luis1118 20 күн бұрын
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 20 күн бұрын
EF is usually pretty good at managing this
@panagiotischatzoglou7249
@panagiotischatzoglou7249 18 күн бұрын
Very nice video! Can you share source code?
@MilanJovanovicTech
@MilanJovanovicTech 18 күн бұрын
In the description (if not then Patreon - can't recall exactly)
@bookfirst-kg3gq
@bookfirst-kg3gq 20 күн бұрын
and also automation test books
@MilanJovanovicTech
@MilanJovanovicTech 20 күн бұрын
Book first, got it
@kashminer4897
@kashminer4897 21 күн бұрын
First comment from kashmir
@MilanJovanovicTech
@MilanJovanovicTech 21 күн бұрын
Blazing speed
@bookfirst-kg3gq
@bookfirst-kg3gq 20 күн бұрын
I would suggest to read DDD books first!
@MilanJovanovicTech
@MilanJovanovicTech 20 күн бұрын
Ok
Are You Accidentally Crippling Your EF Core Queries?
17:18
Milan Jovanović
Рет қаралды 29 М.
Domain-Driven Design: The Last Explanation You'll Ever Need
21:05
Software Developer Diaries
Рет қаралды 8 М.
🕊️Valera🕊️
00:34
DO$HIK
Рет қаралды 20 МЛН
Trapped by the Machine, Saved by Kind Strangers! #shorts
00:21
Fabiosa Best Lifehacks
Рет қаралды 14 МЛН
Completely Get Rid of Null Using This Technique
25:28
Milan Jovanović
Рет қаралды 20 М.
Full Text Search in .NET With PostgreSQL and EF Core
24:13
Milan Jovanović
Рет қаралды 9 М.
Making A WebSocket Server With .NET 8🧑‍💻  [FULLSTACK 2024 VIDEO 1]
18:43
Alex's Dev Den 👨‍💻
Рет қаралды 13 М.
How To Use Domain-Driven Design In Clean Architecture
30:27
Milan Jovanović
Рет қаралды 111 М.
Sqlite Is Getting So Good
28:52
ThePrimeTime
Рет қаралды 188 М.
How is this Website so fast!?
13:39
Wes Bos
Рет қаралды 857 М.
DDD Building Blocks
4:27
Drawing Boxes
Рет қаралды 42 М.
"You're Doing Validation Wrong in .NET" | Code Cop #023
14:44
Nick Chapsas
Рет қаралды 36 М.
5 Design Patterns That Are ACTUALLY Used By Developers
9:27
Alex Hyett
Рет қаралды 297 М.