How To Use Domain-Driven Design In Clean Architecture

  Рет қаралды 113,944

Milan Jovanović

Milan Jovanović

Күн бұрын

Пікірлер: 297
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Get the source code for this video for FREE → the-dotnet-weekly.ck.page/rich-domain-model Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@JOHNSONLOBOlobojony
@JOHNSONLOBOlobojony 2 жыл бұрын
I registered today. How can I get previous new letters? Can you help me
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
@@JOHNSONLOBOlobojony Send me an email at milan@milanjovanovic.tech!
@newraze
@newraze 2 жыл бұрын
Honestly. After few weeks of learning fundamentals of DDD, watching conferences, tutorials, reading blogs and articles about how and what to refactor, why this or that way, when and what set to private/internal or leave public I can say with 100% sure - this is the best explained video concerning this topic. Now its going to be easier for me to refactor my learning projects from anemic to rich model and be motivated to write code again :)
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Hi Mateusz, I am blown away by your feedback. I had to read it a few times out of joy. Your comment makes all my effort in producing these videos worthwhile!
@Flanno91
@Flanno91 2 жыл бұрын
@@MilanJovanovicTech I have to agree with Mateusz. I am currently working on a solo project that I would like to rewrite using DDD and watching your videos as well as Amichai Mantinband's are helping me understand the whole architecture model so well. The best thing is how current this all is because .NET has undergone so many changes in the past few years. A lot of the videos I had been using to learn DDD with .NET are just out of date once it gets to the deeper .NET development. Please please keep it up.
@Top10verse-q1l
@Top10verse-q1l Жыл бұрын
Mateusz ! I am starting DDD training can you please guide me what should be my roadmap so that I dont waste time going here and there please ?
@EmptyGlass99
@EmptyGlass99 2 жыл бұрын
You have a gift for comprehensive and precise communication without unnecessary filler material.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I never thought of it as a gift, but thank you very much 😊
@timurrozhok2685
@timurrozhok2685 Жыл бұрын
I really like how you explain complex concepts and designs in a such simple way. Extra butter to your videos is that examples are not "dead" - you try showing things on cases that are close to real domains. No "Foo" and "Bar" involved, which is really great!
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I've got some awesome DDD videos coming out in about a week 😁
@timurrozhok2685
@timurrozhok2685 Жыл бұрын
@@MilanJovanovicTech I'm looking forward to it 🤩
@16M-w4y
@16M-w4y 9 ай бұрын
Honestly, this is the first video that I fully understand from this channel. I know that the owner of the channel is a high-level person, but the videos he shows are always short. It shows the problem quickly, but in this video I got a very high benefit. I hope the channel owner makes videos like this in the future . in end thanks Milan 😎🎯👨‍💻
@MilanJovanovicTech
@MilanJovanovicTech 9 ай бұрын
Noted!
@33bgatti33
@33bgatti33 Жыл бұрын
Thanks for the wonderful videos you have posted on KZbin. I'm working on a large-scale WPF application, and I've decided to do a DDD design for obvious reasons. However, I struggle with limited contexts and some potential circular references. Let me clarify: I'm working on an application that will measure traffic lights in a production process. The app will also include some serial number generation and other business related stuff. due to size and complexity, I decided to have three projects in the domain layer. Domain.Shared (base classes for Aggregate, ValueObject, Result ...) Domain.Measurement (logic for calculating product measurement results) Domain.SerialPort (Logic for SerialPort, where I specify specifics and client commands). Domain.SerialPort is boundedContext because the user must specify these settings in the user interface, and there are constant changes and versions of the product. The problem I have is that I want to specify a two-way property in EF. if i want to do this i need a field from Domain.Measurment to Domain.Products and a field from Domain.Products to Domainain.Measurment which creates a circular reference between projects. Another option I have is to inherit the entities in the persistence layer and just add the two-way fields there. what would your suggestion be?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
You said you're treating these as separate *bounded contexts*. Then simply define duplicate entities, inside of each bounded context, with the required information. In your EF Core configuration, you can make them use the same database table. Does that make sense?
@33bgatti33
@33bgatti33 Жыл бұрын
@@MilanJovanovicTech Thanks you Milan, It makes a lot of sense to do so. When configuring EF Core i just need to add same name into ToTable property.
@UberEverywhereSKRT
@UberEverywhereSKRT 2 жыл бұрын
Hi great video ! I have two noobie question : 1) at 9:32 , in the SendInvitationCommandHandler, you create an invitation then you add it to the gathering.Invitations list (line 63) then add the invitation in its repo then SaveChanges(). What is the purpose of doing both? Cant we just add the invitation to the repository and SaveChanges OR add the invitation to the gathering.Invitations list and just SaveChanges since EF is tracking? 2) at 24:00 cant we just keep List Attendees but give it a private set? Instead of making it a ReadOnlyCollection and create whole new field private? Ty so much in advance!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
1. The first part will be enough for EF. I'm adding it to the repo just to make it more robust. Can easily be left out. 2. Yes, we can go with that approach.
@krccmsitp2884
@krccmsitp2884 2 жыл бұрын
I liked your step-by-step approach and your explanations about postponed decisions.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks a lot!
@junior.santana
@junior.santana Жыл бұрын
Awesome! I really liked the fact that you didn't just bring a finished solution and showed it, but rather explained the process step-by-step and the reasonings for each decision. That's a clever way of teaching this topic. I'll try to binge-watch your videos about DDD as this is a new subject for me.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I have a recent playlist where I started modeling a domain from scratch, worth checking out
@NatalyaShtikel
@NatalyaShtikel 2 жыл бұрын
Thanks for great video, Milan! 15:07 Since the actual object being returned above is still a List, a caller may cast the result to a List and then add or delete an invitation. Because of this, you should use read-only wrapper by calling .AsReadOnly()
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I tried, won't compile 😅 Do you have a working example?
@michamularczyk7372
@michamularczyk7372 Жыл бұрын
Great video, thank you. One question to 24:30. Shouldn't logic defining rules for calculating whether invitation expired be considered as some kind of business logic and thus belong to Application instead of Domain layer?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Business logic does belong in the Domain
@fadygamilmahrousmasoud5863
@fadygamilmahrousmasoud5863 2 жыл бұрын
i think this is the best explanation so far about how to design a rich domain, Thank you Milan. And i am really waiting for a series in which you start building a project from scratch to see the best practices of design and layers implementation with you, Thank you again for your efforts❤
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you so much! Be sure to check out some of my other videos :)
@fadygamilmahrousmasoud5863
@fadygamilmahrousmasoud5863 2 жыл бұрын
@@MilanJovanovicTech Hello again Milan 😀 I just watched this video multiple times to get more details, but i need to ask you a question because i am working on a personal project now and i decided to go with the clean arch. to practice on it, As i know the idea form this arch is to stop thinking about the data-centric approach and start thinking about the business-rules and behaviour, okay that's great but when i started to design my rules to implement the domain i found myself thinking about the design of the entities and the relationships btn them and FK and this stuff .. so i am right or not because as i understand from this video you already know the relations btn the entities before refactoring ??
@IcaroFelix2023
@IcaroFelix2023 Жыл бұрын
I found your content first by LinkedIn, and I really want to say that you are an amazing teacher.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Glad you think so! I really appreciate that!
@MickaelLY
@MickaelLY Жыл бұрын
Great tutorial and awesome channel ! @11:09 why do you leave the guid as a constructor parameter ? Is that a bad practise to generate the guid directly in the constructor itself?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I like to generate IDs client side when using Guids
@timurrozhok2685
@timurrozhok2685 Жыл бұрын
A definitive *bold like* for this one! One thing that bothers me in this rich domain implementation, and not explicitly mentioned in video, - is that the time retrieval logic moved from the application layer to the domain one. In this example the domain just takes UTC from system clocks, but it may not be the domain's responsibility (and authority!) to act this way. The most suited implementation of a time provider may depend on a specific environment, but higher layers have no control over it now.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Agreed, I kind of "hacked" it for time as I didn't want to bother. But you can either provide at as a method parameter, using a custom ISystemTime abstraction, or the .NET 8 time provider thingy
@timurrozhok2685
@timurrozhok2685 Жыл бұрын
@@MilanJovanovicTech Thanks for the reply! I wish you inspiration and creativity for your incredible work!
@pcclrocks
@pcclrocks Жыл бұрын
Hi Milan, love your videos. I am learning a lot. I would like you to consider changing a setting in your Visual Studio. In Tools -> Options -> Projects and Solutions, select "Track Active Item in Solution Explorer". I suggest this because you move so fast from one class to another. This would help some of the viewers to better identify in which class you are working. Thanks and keep up with the good work.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
This is a year old video 😅 I'd say I'm doing a much better job at it now
@ajaysingh-sc1qt
@ajaysingh-sc1qt 7 ай бұрын
Lucky I came across this video...... and finally got understanding what we were doing wrong. Thanks for the excellent content.
@MilanJovanovicTech
@MilanJovanovicTech 7 ай бұрын
What was the issue you were seeing?
@ajaysingh-sc1qt
@ajaysingh-sc1qt 7 ай бұрын
@@MilanJovanovicTech kept business logics were in handler or business classes. and Entities with only properties.
@bigardibatera
@bigardibatera 2 жыл бұрын
A friend of mine recommended your channel and your videos are awesome! Keep up with the good work man!!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you for watching, Gustavo, I'm glad you liked it! And also thank your friend for me, for recommending my channel 😁
@JOHNSONLOBOlobojony
@JOHNSONLOBOlobojony 2 жыл бұрын
Thank you very much Milan. started getting good exposure into clean architecture and DDD. good luck!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Awesome, I'm sure you are going to enjoy it 😁
@randallhodgson447
@randallhodgson447 Жыл бұрын
In minute 15:33 why would you add the invitation to the _invitations list if you already add it in the repository afterwards?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Just for safety, but with EF Core it's not necessary
@mgame8082
@mgame8082 2 жыл бұрын
Which program are you using for drawing class diagram? 1:10
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
draw.io
@branislavpetrovic7486
@branislavpetrovic7486 2 жыл бұрын
Great video with proper duration for the topic! Excellent example of transforming anemic to rich domain model. Thanks!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you so much Branislav. What is your biggest takeaway from this video?
@branislavpetrovic7486
@branislavpetrovic7486 2 жыл бұрын
@@MilanJovanovicTech It is cleaning handlers from business logic to follow SRP and moving responsibility to gathering domain model for nested properties business logic.
@mehranlabour
@mehranlabour Жыл бұрын
Thanks Milan, You are perfect as always
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Thanks, you too! :)
@max_gus
@max_gus Жыл бұрын
At 22:10 why do we move invitation accept logic to the gathering entity? In my mind accepting an invitation is not responsibility of gathering object
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I'm looking at the Gathering as the aggregate, and want to enforce all operations through it. Of course, you can design the domain in the way you propose and there would be nothing wrong there.
@Mafyou75
@Mafyou75 Жыл бұрын
Insted of returning null, you can use the keyword "default". And thank you for your work Milan :-) you are doing good !
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Thanks for the idea!
@tiagocandeias3969
@tiagocandeias3969 2 жыл бұрын
Milan, needless to say that this was once again a great and awesome video :) Great work picking a somehow complex topic and make it look like a simple action without being lost at the definition hell of DDD :)
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
People learn better by watching/doing then they do by reading. So I had to get creative and think of something practical. Really appreciate the support Tiago!
@natalijagajic4114
@natalijagajic4114 2 жыл бұрын
Really nice topic and great content! As someone working in education, I appreciate your effort to give such nice practical examples.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you Natalija, feedback like yours really motivates me to keep going 😊
@dhanushshetty7840
@dhanushshetty7840 2 жыл бұрын
Found you on LinkedIn. Love design related videos you're posting!!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you very much. I'm glad you like my content so far, I'm trying to improve and get better with every video.
@newguycho
@newguycho 2 жыл бұрын
Great video thanks! Can't wait for the next one to see how you gonna handle unanswered question with sending the email. As you told me on previous video probably it's going to be with mediatr publish notification. Keep up the great work!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Exactly, I want to show a solution using Domain Events, MediatR INotification and the Outbox pattern. Will make for quite a video 😁
@dandoescode
@dandoescode Жыл бұрын
Another excellent video Milan. Watching you do the migration piece by piece is a really easy way to understand the differences between an anemic and rich data model. Which is the next video you eluded to? Domain events or outbox?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Domain events, then Outbox
@sushilb7994
@sushilb7994 2 жыл бұрын
Milan, another great and useful video! I must say you choose video topics wisely and bringing quality content. Would love to see more on domain driven design concepts with the examples.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you very much Sushil. Make sure you watch the new video about Entities to wrap up this topic 😁
@fernandocalmet
@fernandocalmet 2 жыл бұрын
Beautiful content, you have helped me improve aspects of my domain implementation. Please continue to release more content Milan, this type of content motivates me a lot in my professional career. Thank you very much for your contribution!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Hi Fernando, I'm happy you like the video. Which part made the biggest impact on you?
@Kasiux
@Kasiux Жыл бұрын
15:56 - Why don't you create a static "Create" method on the Invitation Entity?
@Kasiux
@Kasiux Жыл бұрын
Probably because there is no Invitation without a Gathering entity and the constructor is internal ... ^^
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
@@Kasiux Yeah, I'm making it internal and shifting the responsibility to the Gathering entity for creating it. Could be approached differently, but this was my thought process.
@Kasiux
@Kasiux Жыл бұрын
@@MilanJovanovicTech Sounds good. Keep up the great work, your videos are the best!
@SoheilKhosroshahi
@SoheilKhosroshahi 10 ай бұрын
I couldn't understand why did you something like that with Lists (in 15:18) I think that EF core can handle all of them... or may be you did this because you wanted to use other ORMs like dapper? also i dindn't like adding SendInvitation into Entity... i think it's a buisness rule, and I have to implement that on application layer. note that: i'm not a native guy so some times i can't translate my right meaning i'm Sorry if I was disrespectful, I like your videos very much
@MilanJovanovicTech
@MilanJovanovicTech 10 ай бұрын
DDD is about pushing business logic to the domain. So that's the motivation behind the decisions.
@Eless88
@Eless88 2 жыл бұрын
Hi, Milan I'm not sure, that changing the Invitation's properties inside of the gathering is a good way because it looks like a side-effect. I think, that the Gathering class should only have the possibility to check the expiration and AddAttendee(attendee) method
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You can model it how you think is best, I think I explained my reasoning for why I chose to do it like this
@mattmarkus4868
@mattmarkus4868 2 жыл бұрын
A Gathering does in fact own the Invitation. You can't have an Invitation without a Gathering, it wouldn't make sense. But a Gathering object can have no invitations, that seems totally valid. It makes sense to me to isolate that. I don't know what you mean by side effect. Why have multiple ways of creating or changing an Invitation that's not known by the parent Gathering? What is this side effect concern?
@mattmarkus4868
@mattmarkus4868 2 жыл бұрын
@E1ess , it's also good encapsulation of any logic required to create/send an invitation. It's all nicely hidden inside the owner Gathering instance. That is further illustrated by making those children a read-only collection. All a user can do is say, "SendInvitation() and return it to me. Whatever it does I don't care". And that allows that logic to change without affecting users (although, it is core logic and isn't likely to change but if it does it changes for all applications using it). The Handlers is where app-specific logic goes. Domain-specific logic goes in the domain layer. I like this channel so far.
@zhivkopetkov4133
@zhivkopetkov4133 2 жыл бұрын
I found you via LinkedIn, nice videos for sure, keep the good work
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you very much. Any content wishes? 😁
@rafapioli75
@rafapioli75 2 жыл бұрын
Great explanation! Thanks for your efforts to create this kind of content! I learned a lot from this video.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
That's excellent. My hope is you will continue to find value in the future videos
@marcinaumiler3722
@marcinaumiler3722 Жыл бұрын
Hi Milan, you have truly great skills to deliver somewhat complex content in clear and concise way:) I've got two question (unless you already covered those in another video): 1. What do you think about abstracting away dependency on DateTime in your domail model (e.g. to make testability easier)? 2. What is your opinion on returning types from your methods representing commands (e.g. Invite) instead of keeping return type void and follow CQS.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
1. I agree that would be a good idea, but I didn't want to bother with it at the time 2. I'm not sure what that would look like. Have any example?
@Swzvtlbngfd
@Swzvtlbngfd Жыл бұрын
Thank you for this wonderful video showing step by step, moving logic from Command Handlers to Entities itself. I have a query on implementing the same for Database First approach where the DB is already created for us. In such cases,we auto generate the entities using Scaffold-DbContext command and every time there is change in DB design, the entities are auto generated. How to tackle those situations? Do we put all domain logic in separate partial classes? Thanks again for this series.. I have been using N-Tier architecture for so long that this is a whole new fresh air of learning and I am very happy to be able to learn this new architecture.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
You can even create your Domain entities as separate classes from your persistence entities. You would have to handle mapping between the two, however.
@shivamdeshmukh852
@shivamdeshmukh852 8 ай бұрын
I thoroughly enjoyed your content and wish I had discovered it a year ago. Your explanation of concepts is both precise and on point. My only suggestion is to consider developing a structured path that guides learners from creating projects from scratch to a level where they can confidently produce a fully functional application, perhaps through a series of 10 videos progressing from beginner to advanced levels.
@MilanJovanovicTech
@MilanJovanovicTech 8 ай бұрын
Welcome aboard! And that's a great idea :)
@ПётрЗахаревич-э8и
@ПётрЗахаревич-э8и 2 жыл бұрын
Great video, thanks, the short question, why do we use method name Gathering.SendInventation considering the fact that method does not perform any 'sending' logic and just add a newly created Inventation instance to the Gathering?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You are expressing intent via the method name. In terms of the domain, creating a new invitation instance is "sending the invitation"
@pburczyn
@pburczyn Жыл бұрын
​@@MilanJovanovicTech I don't agree with you :) the 'method name' should reflect what it does, not what the intention is (as a developer I would be confused if I saw a method name like that).
@VladyslavHorbachov
@VladyslavHorbachov 2 жыл бұрын
Great job, bro👍👍 Waiting for the next videos
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks!
@paulbarton2280
@paulbarton2280 2 жыл бұрын
Great content, keep it up. I would like to see a video on separating your domain via bounded contexts and how that would work with EF.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
That's a great topic, but I want to cover some basics first before tackling that. And I also need a more complex domain for the explanation to make sense. I will think about it, maybe I can re-work this idea to fit the bill.
@mwaseemzakir
@mwaseemzakir 2 жыл бұрын
Before going to bed I preferred to watch this video instead of listening to music 😍
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
This is a first for me 😁 I hope that you enjoyed the video and found it impactful!
@haruundk
@haruundk 2 жыл бұрын
How would you go about translation to different languages? I mean mostly static error messages etc.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I would leave that for the frontend most likely
@hlazunov
@hlazunov 2 жыл бұрын
I would recommend reading about DDD starting from Von Vernon's "red" book and then reading Eric Evans's book.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I've read Eric's book, but haven't read the one from Vaughn yet.
@sajagjain7169
@sajagjain7169 2 жыл бұрын
Hi Milan, Is there a way to restrict private fields assignment in certain cases within the model? Context: Hypothetically if I save age and birthday both in db anytime I update birthday it should update the age. But anyone within the model can still set those fields by directly assigning values to the private fields.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Not much you can do about that. But consider this: if someone is setting the value of a private field - you will know. You can't really miss that in the code, so you're much more likely to catch it and fix it accordingly. Right?
@sajagjain7169
@sajagjain7169 2 жыл бұрын
@@MilanJovanovicTech I agree with you. Do you think having a convention here might help??
@danilodjokic5303
@danilodjokic5303 2 жыл бұрын
Milan, what is in your opinion the advantage of having a static factory method instead of implementing the full factory pattern ?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
With a static factory approach I have access to private/protected methods on the Entity. I tend to use this fact to simplify the behavior by having smaller methods. Also, I'll show an example of static factory with domain events. I didn't find much use for the full factory pattern. Perhaps I didn't run into a problem that really needed it.
@mohamedasem6523
@mohamedasem6523 Жыл бұрын
great video, I want to know what's the alternative for returning null, did you make a video for that?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Return a Result object
@hosseinnarimanirad2953
@hosseinnarimanirad2953 2 жыл бұрын
Good! wasn't it better to call AsReadOnly method to return IReadOnlyCollection. This way (not using this method) clients may simply cast the collection as List and modify it
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
ToList will return a new list, different memory reference. And you can't cast it. Did you try?
@hosseinnarimanirad2953
@hosseinnarimanirad2953 2 жыл бұрын
@@MilanJovanovicTech Where did you use ToList? In this video you simply return the backing field in the getter. Anyway I think its better to return the readonly type to emphasis that the client cannot change it.
@Top10verse-q1l
@Top10verse-q1l Жыл бұрын
Good explanation I am following your tutorial but I have one question you put send invitation inside gathering class isnt it against S of Solid principles ? because our class is not only responsible for create gathering but also it is sending invitation ?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Think of the Gathering as a whole. You can't apply single responsibility to absolutely everything out there.
@laptoprecaia.k.aagentsmith3328
@laptoprecaia.k.aagentsmith3328 Жыл бұрын
I am amazed. Thank you Milan.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
You're welcome :)
@marhoily
@marhoily 2 жыл бұрын
When you are doing data validation video, could you take an example of the nature of "there should be no 2 members with the same name\email"? I'm interested in how you are going to avoid the data racing condition, when there are 2 simultaneous requests to create a user with the same name. I understand how the optimistic concurrency works on the level of an entity, but what are you going to do when it is not practical to aggregate all the data necessary for the validation in one entity?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
The next video will be more about Domain level validation. I consider the "unique email" rule more of an Application level validation, but I will definitely cover that in one of the coming videos. Thanks for the suggestion! As far as the implementation - here you can just rely on a unique index at the database level. It is atomic by design.
@issacproton7885
@issacproton7885 2 жыл бұрын
Wow...Awesome...just loved it...please keep it up for the community who encourage to learn....please make a video on productivity shortcut on visual studio if you could so that everyone can get more benefits out of it.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
The best productivity hack is: ONLY USE THE KEYBOARD. This will force you to learn all of the keyboard shortcuts, and you will be much faster. Guaranteed.
@issacproton7885
@issacproton7885 2 жыл бұрын
@@MilanJovanovicTech hmm..strongly agreed!
@tiagocandeias3969
@tiagocandeias3969 2 жыл бұрын
@Milan Was reviweing this vídeo and I need to ask. What tool you use to make the class diagram in 1:20?🤣 Thank you
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Hey Tiago :) It's built with draw.io but there's a setting to make it look "cartoonish" 😁
@tiagocandeias3969
@tiagocandeias3969 2 жыл бұрын
@@MilanJovanovicTech I have to look for that setting, those look awesome!
@vivekkaushik9508
@vivekkaushik9508 2 жыл бұрын
Which keyboard do you use? I really like the sound of it. Blue switches?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Nothing fancy: Logitech K120
@nothingisreal6345
@nothingisreal6345 2 жыл бұрын
Rule of thumb: whenever you write a couple of lines that all do obj.Property= or obj.DoSomething() - consider moving that code into the implementation of the object - especially if you do the same or very similar changes elsewhere. Certainly, a property like NumberOfAttendess (which is redundant as it is equal to the count of the list) must never be writeable from outside.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Good point, that way when you add a new property you only have to change that one method.
@ravindranathwi
@ravindranathwi 2 жыл бұрын
I see return Unit.value where is that. Can't see it in the class anywhere
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Coming from MediatR library
@wvanlosser
@wvanlosser 2 жыл бұрын
Nice video! Looking forward to the next one.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
What did you like the most about this one?
@wvanlosser
@wvanlosser 2 жыл бұрын
@@MilanJovanovicTech I've been reading and watching alot of DDD and Clean Architecture related content, but most content start from scratch. It was nice to see what steps you took to move from an anemic model to a rich model. One thing that I do struggle with is that most examples are relatively simple (models with but a small number of properties). I can see these model classes exploding when the models become bigger. Maybe that's also a sign that the model isn't correct (maybe doing too much in one place). Would love to see some more complex examples, although I can understand that those might not be very good for learning people new stuff. Anyway keep up the good work! I'm really enjoying the content so far and you explain things very understandibly. Also the pace and length of the videos are very good in my opinion 😃
@zawette
@zawette 2 жыл бұрын
Very well put video !
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you!
@ghaiathaltrabulsi135
@ghaiathaltrabulsi135 2 жыл бұрын
Thanks Milan. Shouldn't the commands and queries live in Domain and theirs handlers live in Application? Is there any wrong with this approach?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I don't agree with that school of thought. To me, use cases - under which I also count commands/queries - fall under Application level concerns.
@glauberbrennon
@glauberbrennon 2 жыл бұрын
@@MilanJovanovicTech this is an interesting topic! when we talk about "plain hexagonal architecture" we would put those definitions in our "core" layer bcs this module wants to define everything from the inside-out! but when we talk about, what i like to call, "cleanxagonal architecture" everything related to "commands" or "queries" should be defined in the application layer
@VladaNish
@VladaNish 2 жыл бұрын
Great job and another great video.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks! What did you like the most?
@VladaNish
@VladaNish 2 жыл бұрын
@@MilanJovanovicTech practical train of thought and the way how it was presented.
@VladaNish
@VladaNish 2 жыл бұрын
BTW, as per sending email and saving data in db, old way would be wrapping with transaction and commit that one when email is sent successfully but I find it a bit ugly (commit may also fail), I would prefer something like hangfire or quartz (they can fail / keep failing but it's "cleaner")
@ThugLifeModafocah
@ThugLifeModafocah Жыл бұрын
What is the problem of the logic inside de constructor or inside of the static factory method create? Why one approach is better than the other?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Constructors can be called by ORMs, for example EF Core does that. So if you place something wide a side effect in the constructor it can lead to undesired behavior.
@ThugLifeModafocah
@ThugLifeModafocah Жыл бұрын
@@MilanJovanovicTech humm, got it. Thanks for the answer.
@sahinhanay3692
@sahinhanay3692 Жыл бұрын
Isn't writing logic in the entity class against solid's s principle? Doesn't it make more sense to apply the factory pattern in a separate place?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Not in DDD
@JLPA42
@JLPA42 2 жыл бұрын
Thanks for the video. Is there any added value of using a static factory instead of new Gathering()? The switch/case statement can be a method of the Gathering entity that is called on the constructor as part of the minimum validations in order to build a valid Gathering.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You can get some fun behavior with ORMs and constructors with logic. EF for example will find the best constructor (even a private one) to create a new instance of the class. I was bitten by this before, so I tend to be cautious. Also, sometimes it's quite useful for your factory method to be async. For example, if you pass in some service interface to perform a check. In this case you can't use a constructor at all.
@fxandrei
@fxandrei Жыл бұрын
Do you have a link with the book you mentioned about DDD ?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215
@adisilagy
@adisilagy 2 жыл бұрын
Hi Milan, In this video you created a static method responsible for creating a gathering entity. How would you create the gathering entity when getting the records from db using Dapper. If I am not mistaken the Dapper mapper will not be able to create the entity right?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Indeed, this approach would be tough to work with for Dapper. With EF it works without much trouble. You can get by with a factory method for Dapper. So fetch the raw data from SQL, and then pass it to your factory to construct the entity properly. Alternatively, you can build entity factories in the Domain layer - but it'll end up being a lot of code with little benefit.
@adisilagy
@adisilagy 2 жыл бұрын
There are a lot of options regarding using Dapper or EF. I would like to hear yours. From your videos I understand that you are a fan of EF, but I also heard you say in two of your videos that for boosting performance you would use Dapper.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
@@adisilagy Just create a static method that accepts primitive types. Load from database using Dapper. Call the static method, which will transform values into rich domain model. Perhaps I should make this into a video?
@adisilagy
@adisilagy 2 жыл бұрын
@@MilanJovanovicTech it would be great if you can create one
@adisilagy
@adisilagy 2 жыл бұрын
@@MilanJovanovicTech I think that a general video showing how using Dapper with the repository pattern and rich domain model would be an interest to many developers
@kauegatto
@kauegatto Жыл бұрын
Might be a really really dumb question, however, let's suppose I want a User Crud, in this more spefic scenario I want to create an User account. Then should the Controller instantiate a User (by using the Controller) and then pass the User object to the repository and have faith on the User validation since an error wasn't thrown? Also, what do you think of Services? When should we create them? I've heard of some people that Services should be an action (verb) that makes use of different domain objects.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I'm unsure about your first question, but if I understood correctly: The approach with Controllers is fine if you want to place logic in controllers. I use services mostly to abstract external services. And occasionally as domain services. Domain services to encapsulate some business logic, jo external concerns.
@rezarezash
@rezarezash 2 жыл бұрын
Hi Milan, Is there a separate video for the Application project?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Closest is CQRS video
@mohammadalisehhat2027
@mohammadalisehhat2027 4 ай бұрын
Awesome! Thanks a lot for this great video. I have a concern, if I set some members of a class as internal, how could I write test for these members in another assembly? In that case we don't have access to these internal members! Is there any solution for this problem?
@MilanJovanovicTech
@MilanJovanovicTech 4 ай бұрын
Expose internal members to the unit test assembly
@Pest87
@Pest87 Жыл бұрын
An excellent video. What do you think about the opinion that domain driven design works well only if we know all of the project's business logic from the beginning and that this rarely happens in the real world? And some folks claim in such situation it is better to use the more old school multi layer architecture with presentation, business and database layers and annemic models. It would be great if you can shoot a video about comparing the pros and cons of both.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I think your domain model needs to (and will) evolve over time. Nobody starts with a complete domain model. You end up with one, after many iterations.
@microtech2448
@microtech2448 Жыл бұрын
This could seem simple with a few handful properties on an entity, but is seems overloading entity with methods. And creating entity using parameterized create method would blow up when we have entities with close to hundred properties. Isn't there some simple way to create them if we need to deal with hundreds of properties in the entity and separate out other business logic as you doing everything in Create method within entity class, which makes it quite messy.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Doesn't hundreds of properties make it even messier? We should fix that first
@microtech2448
@microtech2448 Жыл бұрын
​@@MilanJovanovicTechthat's what my concern is. How would deal with large entity and creating them as parameterized method or constructor. What is good alternative approach? Also, your entities are directly resembling the Db tables and columns, which make them kind of tightly coupled because you are saving or retrieving entities directly instead of any mapper in between. There should be mapper between domain entity and actual db entity, what's your opinion and how to restructure your code accordingly?
@prateekagrawal1907
@prateekagrawal1907 2 ай бұрын
Hi @Milan, Thanks for the video very well explained. Just wanted to know if we are moving SendInvitaton method to the Gathering entity, is it breaking SRP concepts?
@MilanJovanovicTech
@MilanJovanovicTech 2 ай бұрын
Why is breaking SRP?
@athlentral-1491
@athlentral-1491 3 ай бұрын
Hi Milan, your videos are very good! I enjoyed every single one of them so far. Right now I just have a quick question in relation to what you said about “who owns”. I noticed the gathering class logic increased because of that. Now gathering ended up also being concerned with sending and accepting invitations. I would like to know, after refactoring, is the gathering class still adhering to single responsibility principle? Thanks you for your videos!!!
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
I don't like at the SRP from that perspective when it comes to the domain aggregates.
@jakubsuchybio
@jakubsuchybio Жыл бұрын
Hi, just a dev tip: when you were replacing that constructor parameter invititationsValidBeforeInHours, you could do a multi-cursor there with Ctrl+D 2x times for the 3 occurences and not 3x times navigating with a mouse ;-) but maybe you already know this after 9months of publication of this video
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
You sometimes forget things like that when you record videos 😅
@rezarezash
@rezarezash 2 жыл бұрын
Thanks for the videos and effort - Just a comment -The video is supposed to cover the domain but you started with the application and commands - Also, it could be more helpful if you create the domain entities step by step or at least a couple of them. Thank you.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Take a look at the other videos also
@emmanuelcaulin4395
@emmanuelcaulin4395 8 ай бұрын
Do you use async every time you call db operations even if it only returns 1 row of data or 1 column?
@MilanJovanovicTech
@MilanJovanovicTech 8 ай бұрын
Yes
@samehkeshta89
@samehkeshta89 2 жыл бұрын
Great tutorial, keep it up.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks a lot!
@DeejayWazzouille
@DeejayWazzouille 2 жыл бұрын
Hi ! I read that ctor should be private in model to avoid creating anywhere except in the model himself. But how do you convert object coming from databse (objectDTO) to the object domain model without a public constructor ? I could use create, but it doesn't work with the ID property which shouldn't be changed
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You can create a factory method for that
@davorzganjer5291
@davorzganjer5291 10 ай бұрын
Nice video, self-explanatory. In this case, business logic is encapsulated in the domain model and it's not dealing with DB but let's assume that business logic needs some interaction with DB, meaning if-else would call different DB logic (repository). By design, we can't inject a repository to get needed data but how would you approach DB interactions from your domain model?
@davorzganjer5291
@davorzganjer5291 10 ай бұрын
I think I found it kzbin.info/www/bejne/m3SaeIB9frdnfdk
@MilanJovanovicTech
@MilanJovanovicTech 10 ай бұрын
There are three options, I talked about them here: kzbin.info/www/bejne/m3SaeIB9frdnfdk
@nobel03
@nobel03 2 жыл бұрын
How do you handle domain objects and persistence? Because a domain object's structure may not be suitable for relational DB persistence.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I try to not think about persistence when designing my domain model, because it will be constraining to design an ideal domain model that way. However, I will show you how to convert this domain model into a normalized relational model in one of the future videos!
@tienlam5599
@tienlam5599 2 жыл бұрын
@@MilanJovanovicTech Is that so you will design domain objects before persistence ? Then convert from domain objects to persistence ?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
​@@tienlam5599 That's why it is called "Domain-Driven". The point is that if you concern your self with persistence while modeling your domain, you will make some decisions that impact your model so that it can be translated to a certain database model. This goes against the point of using DDD. I will show you in a future video how to persis this model using EF, it's not so complicated.
Жыл бұрын
I prefer when business logic is everywhere, yet clearly identified. For example, the presentation layer could know the format of a US zip code. I prefer when layers are not distinct projects (file). A class could be able to talk to DB via basic interface eg. DAO. Well I also like to go for twho parts: Backend (endpoints) and frontend (CSS+HTML+Javascript).
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
What's the benefit of having business logic everywhere?
Жыл бұрын
@@MilanJovanovicTech Speed, and I also duplicate the logic once in the stored proc and once in the front of back end - no need to hit the db to know something is invalid and another foreign application or user may run the [safe] stored procedure without proper up-front validation. I'm for simple code (fewest indirections, little or no function/class injection).
@peymanebrahimi7756
@peymanebrahimi7756 2 жыл бұрын
Thanks for great content. Saga can be used for db saving and sending email transactions. Masstransit would be a great content to cover. I really appreciate the concise and excellent way of explaining.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you for the content suggestion!
@Embriiii
@Embriiii 2 жыл бұрын
Another step could be encapsulation of the wole collection into an "Attendees" class. I'm curiouse about a video on Domain Services, maybe with the distintion about impure and pure domain services. 👍
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I haven't done custom collections. What do you think it brings? Domain Services video is a good topic, I'll add it to my list.
@rodrigoramirez1031
@rodrigoramirez1031 2 жыл бұрын
Hey Milan, great content! thanks for sharing. I've seen before that instead of having the repository independent of the unit of work (as you do on your video) there are people that implement it in a way that the unit of work contains the repositories such as: _unitOfWork.PersonRepository.AddPerson(person) Any thoughts on that? just curious! Thanks!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I like separate repository interfaces because then it is obvioud which queries you can find in a command handler for example. With UoW + Repo combined it's not as obvious.
@René-k5i
@René-k5i 2 ай бұрын
Hi Milan, you mention a next video about "How to deal with external dependencies", can you add a link?
@MilanJovanovicTech
@MilanJovanovicTech 2 ай бұрын
Possibly this one: kzbin.info/www/bejne/m3SaeIB9frdnfdk
@cdsmith8528
@cdsmith8528 2 жыл бұрын
I'd be interested in if you are going to raise the issue of, when you are submitting updates, to not allow an invalid domain entity to be constructed - ie, not applying updated values to an entity unless you can first validate that the new values won't put the entity into an invalid state especially when dealing with complex aggregate entities.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
My approach is to enforce these kinds of constraint by design. Your domain should expose such an API that you can only create entities in the correct state, and also be allowed to only update them into a new correct state. How do you approach this?
@cdsmith8528
@cdsmith8528 2 жыл бұрын
@@MilanJovanovicTech I prefer to do the same thing; enforce by design. In the past, I have had to retrieve the aggregate entity and generate a DTO and send that along with the command object with the new values to a business rules engine where all validation was kept as a separate system.
@alexwexov4298
@alexwexov4298 2 жыл бұрын
Great video !
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks!
@wellyngtond2
@wellyngtond2 2 жыл бұрын
I think, for the case of "sendEmail" error, the best approach would be put the email in a queue or in a table to some cron job get end send it.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Yup, that's the proper approach. Store it somewhere, and process it later.
@pathummadhusanka4698
@pathummadhusanka4698 Жыл бұрын
Why sendInvitation method inside Gathering class? Shouldn't it be in invitation class? What am I missing here? 🤔
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Gathering is the aggregate root
@pathummadhusanka4698
@pathummadhusanka4698 Жыл бұрын
@@MilanJovanovicTech thank you ❤️
@ugochukwuumerie6378
@ugochukwuumerie6378 2 жыл бұрын
NIce content, await NextVideoAsync(). For the email part (hope this is not an overkill), you would probably use the Mediatr notification and publish to something that is not on the main thread and might not be awaitable like hangfire or an In Memory Background Service to handle retries even if it fails.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
That's exactly the idea. I want to show how we can use the Domain Events pattern for offloading certain tasks to the background.
@ugochukwuumerie6378
@ugochukwuumerie6378 2 жыл бұрын
@@MilanJovanovicTech Awesome 👌
@patrykklimas4398
@patrykklimas4398 2 жыл бұрын
What do you think about defining repository interface in the Domain layer?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
I think it's perfectly fine since it's only a data contract. And the way I use it to return only entities, so there is no pollution of the interface with other classes
@jacobnunez7678
@jacobnunez7678 Жыл бұрын
Hi Milan, could you recommend me some books about clean architecture and cqrs?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Clean Architecture by Uncle bob
@jacobnunez7678
@jacobnunez7678 Жыл бұрын
@@MilanJovanovicTech Thanks.
@MdArifulIslam-ov9yd
@MdArifulIslam-ov9yd Жыл бұрын
Awesome ⚡⚡⚡⚡👍👍
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Thank you! Cheers!
@nettoaoquadrado
@nettoaoquadrado 2 жыл бұрын
Amazing!!!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you! Cheers!
@Skuerke95
@Skuerke95 2 жыл бұрын
For the gathering, you can just use a record. No need for private setters or separate ctors.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
But then you end up with an Anemic and Immutable domain model. How will that work?
@AlexanderBushuev-p5h
@AlexanderBushuev-p5h 2 ай бұрын
Thank for video, but do not forget about the principles of SOLID, especially about the first one ;) and it's really a bit strange to set the status expired if the participant limit is exceeded
@MilanJovanovicTech
@MilanJovanovicTech 2 ай бұрын
Thanks for the feedback!
@mahmoudalballah3387
@mahmoudalballah3387 Жыл бұрын
Awesome!
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Glad you think so!
@MayurNagrale
@MayurNagrale Жыл бұрын
Can anyone help me create that project structure where inside solution we will be having src folder and and all the project inside that src?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Just add a solution level folder in VS
@ThingsTK
@ThingsTK 10 ай бұрын
are both the domain model and entity model (EFCore model) same? is there any difference in domain model and entity? generally my entities are suffixed with Entity (InvitationEntity) and domain without any suffix.
@MilanJovanovicTech
@MilanJovanovicTech 10 ай бұрын
I keep them the same when I can, less things to manage
What Is An Entity? | Domain-Driven Design, Clean Architecture, .NET 6
10:14
Domain Modeling with Domain-Driven Design (From Scratch)
18:05
Milan Jovanović
Рет қаралды 27 М.
The Lost World: Living Room Edition
0:46
Daniel LaBelle
Рет қаралды 27 МЛН
Counter-Strike 2 - Новый кс. Cтарый я
13:10
Marmok
Рет қаралды 2,8 МЛН
Война Семей - ВСЕ СЕРИИ, 1 сезон (серии 1-20)
7:40:31
Семейные Сериалы
Рет қаралды 1,6 МЛН
The Beginner's Guide to Clean Architecture
13:19
Milan Jovanović
Рет қаралды 33 М.
Modeling a Domain With Domain-Driven Design From Scratch | DDD
19:10
Milan Jovanović
Рет қаралды 94 М.
7 Design Patterns EVERY Developer Should Know
23:09
ForrestKnight
Рет қаралды 158 М.
Domain-Driven Refactoring - Jimmy Bogard - NDC London 2022
1:00:03
NDC Conferences
Рет қаралды 47 М.
How to Use the Domain Event Pattern | DDD, Clean Architecture, .NET 6
9:48
Domain-Driven Design: The Last Explanation You'll Ever Need
21:05
Software Developer Diaries
Рет қаралды 15 М.
Is an ANEMIC Domain Model really that BAD?
10:36
CodeOpinion
Рет қаралды 18 М.
May The Power of DOMAIN EVENTS Be With You!
22:13
Codewrinkles
Рет қаралды 7 М.
Making Tradeoffs With Domain-Driven Design (DDD CAP Theorem)
11:29
Milan Jovanović
Рет қаралды 16 М.
The Lost World: Living Room Edition
0:46
Daniel LaBelle
Рет қаралды 27 МЛН