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
@JOHNSONLOBOlobojony2 жыл бұрын
I registered today. How can I get previous new letters? Can you help me
@MilanJovanovicTech2 жыл бұрын
@@JOHNSONLOBOlobojony Send me an email at milan@milanjovanovic.tech!
@newraze2 жыл бұрын
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 :)
@MilanJovanovicTech2 жыл бұрын
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!
@Flanno912 жыл бұрын
@@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 Жыл бұрын
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 ?
@EmptyGlass992 жыл бұрын
You have a gift for comprehensive and precise communication without unnecessary filler material.
@MilanJovanovicTech2 жыл бұрын
I never thought of it as a gift, but thank you very much 😊
@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 Жыл бұрын
I've got some awesome DDD videos coming out in about a week 😁
@timurrozhok2685 Жыл бұрын
@@MilanJovanovicTech I'm looking forward to it 🤩
@16M-w4y8 ай бұрын
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 😎🎯👨💻
@MilanJovanovicTech8 ай бұрын
Noted!
@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 Жыл бұрын
I have a recent playlist where I started modeling a domain from scratch, worth checking out
@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 Жыл бұрын
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 Жыл бұрын
@@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.
@fadygamilmahrousmasoud58632 жыл бұрын
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❤
@MilanJovanovicTech2 жыл бұрын
Thank you so much! Be sure to check out some of my other videos :)
@fadygamilmahrousmasoud58632 жыл бұрын
@@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 ??
@krccmsitp28842 жыл бұрын
I liked your step-by-step approach and your explanations about postponed decisions.
@MilanJovanovicTech2 жыл бұрын
Thanks a lot!
@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 Жыл бұрын
This is a year old video 😅 I'd say I'm doing a much better job at it now
@IcaroFelix2023 Жыл бұрын
I found your content first by LinkedIn, and I really want to say that you are an amazing teacher.
@MilanJovanovicTech Жыл бұрын
Glad you think so! I really appreciate that!
@tiagocandeias39692 жыл бұрын
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 :)
@MilanJovanovicTech2 жыл бұрын
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!
@branislavpetrovic74862 жыл бұрын
Great video with proper duration for the topic! Excellent example of transforming anemic to rich domain model. Thanks!
@MilanJovanovicTech2 жыл бұрын
Thank you so much Branislav. What is your biggest takeaway from this video?
@branislavpetrovic74862 жыл бұрын
@@MilanJovanovicTech It is cleaning handlers from business logic to follow SRP and moving responsibility to gathering domain model for nested properties business logic.
@bigardibatera2 жыл бұрын
A friend of mine recommended your channel and your videos are awesome! Keep up with the good work man!!
@MilanJovanovicTech2 жыл бұрын
Thank you for watching, Gustavo, I'm glad you liked it! And also thank your friend for me, for recommending my channel 😁
@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 Жыл бұрын
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 Жыл бұрын
@@MilanJovanovicTech Thanks for the reply! I wish you inspiration and creativity for your incredible work!
@natalijagajic41142 жыл бұрын
Really nice topic and great content! As someone working in education, I appreciate your effort to give such nice practical examples.
@MilanJovanovicTech2 жыл бұрын
Thank you Natalija, feedback like yours really motivates me to keep going 😊
@sushilb79942 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
Thank you very much Sushil. Make sure you watch the new video about Entities to wrap up this topic 😁
@mwaseemzakir2 жыл бұрын
Before going to bed I preferred to watch this video instead of listening to music 😍
@MilanJovanovicTech2 жыл бұрын
This is a first for me 😁 I hope that you enjoyed the video and found it impactful!
@JOHNSONLOBOlobojony2 жыл бұрын
Thank you very much Milan. started getting good exposure into clean architecture and DDD. good luck!
@MilanJovanovicTech2 жыл бұрын
Awesome, I'm sure you are going to enjoy it 😁
@rafapioli752 жыл бұрын
Great explanation! Thanks for your efforts to create this kind of content! I learned a lot from this video.
@MilanJovanovicTech2 жыл бұрын
That's excellent. My hope is you will continue to find value in the future videos
@newguycho2 жыл бұрын
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!
@MilanJovanovicTech2 жыл бұрын
Exactly, I want to show a solution using Domain Events, MediatR INotification and the Outbox pattern. Will make for quite a video 😁
@NatalyaShtikel2 жыл бұрын
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()
@MilanJovanovicTech2 жыл бұрын
I tried, won't compile 😅 Do you have a working example?
@paulbarton22802 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
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.
@Mafyou75 Жыл бұрын
Insted of returning null, you can use the keyword "default". And thank you for your work Milan :-) you are doing good !
@MilanJovanovicTech Жыл бұрын
Thanks for the idea!
@dhanushshetty78402 жыл бұрын
Found you on LinkedIn. Love design related videos you're posting!!
@MilanJovanovicTech2 жыл бұрын
Thank you very much. I'm glad you like my content so far, I'm trying to improve and get better with every video.
@nothingisreal6345 Жыл бұрын
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 Жыл бұрын
Good point, that way when you add a new property you only have to change that one method.
@ajaysingh-sc1qt6 ай бұрын
Lucky I came across this video...... and finally got understanding what we were doing wrong. Thanks for the excellent content.
@MilanJovanovicTech6 ай бұрын
What was the issue you were seeing?
@ajaysingh-sc1qt6 ай бұрын
@@MilanJovanovicTech kept business logics were in handler or business classes. and Entities with only properties.
@mehranlabour Жыл бұрын
Thanks Milan, You are perfect as always
@MilanJovanovicTech Жыл бұрын
Thanks, you too! :)
@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 Жыл бұрын
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?
@hlazunov2 жыл бұрын
I would recommend reading about DDD starting from Von Vernon's "red" book and then reading Eric Evans's book.
@MilanJovanovicTech2 жыл бұрын
I've read Eric's book, but haven't read the one from Vaughn yet.
@shivamdeshmukh8527 ай бұрын
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.
@MilanJovanovicTech7 ай бұрын
Welcome aboard! And that's a great idea :)
@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 Жыл бұрын
Domain events, then Outbox
@VladyslavHorbachov Жыл бұрын
Great job, bro👍👍 Waiting for the next videos
@MilanJovanovicTech Жыл бұрын
Thanks!
@zawette2 жыл бұрын
Very well put video !
@MilanJovanovicTech2 жыл бұрын
Thank you!
@issacproton78852 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
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.
@issacproton78852 жыл бұрын
@@MilanJovanovicTech hmm..strongly agreed!
@wvanlosser2 жыл бұрын
Nice video! Looking forward to the next one.
@MilanJovanovicTech2 жыл бұрын
What did you like the most about this one?
@wvanlosser2 жыл бұрын
@@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 😃
@fernandocalmet2 жыл бұрын
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!
@MilanJovanovicTech2 жыл бұрын
Hi Fernando, I'm happy you like the video. Which part made the biggest impact on you?
@laptoprecaia.k.aagentsmith3328 Жыл бұрын
I am amazed. Thank you Milan.
@MilanJovanovicTech Жыл бұрын
You're welcome :)
@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 Жыл бұрын
You sometimes forget things like that when you record videos 😅
@hosseinnarimanirad29532 жыл бұрын
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
@MilanJovanovicTech2 жыл бұрын
ToList will return a new list, different memory reference. And you can't cast it. Did you try?
@hosseinnarimanirad29532 жыл бұрын
@@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.
@VladaNish2 жыл бұрын
Great job and another great video.
@MilanJovanovicTech2 жыл бұрын
Thanks! What did you like the most?
@VladaNish2 жыл бұрын
@@MilanJovanovicTech practical train of thought and the way how it was presented.
@VladaNish2 жыл бұрын
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")
@UberEverywhereSKRT2 жыл бұрын
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!
@MilanJovanovicTech2 жыл бұрын
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.
@samehkeshta892 жыл бұрын
Great tutorial, keep it up.
@MilanJovanovicTech2 жыл бұрын
Thanks a lot!
@Embriiii2 жыл бұрын
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. 👍
@MilanJovanovicTech2 жыл бұрын
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.
Жыл бұрын
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 Жыл бұрын
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).
@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 Жыл бұрын
You can even create your Domain entities as separate classes from your persistence entities. You would have to handle mapping between the two, however.
@peymanebrahimi77562 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
Thank you for the content suggestion!
@Eless882 жыл бұрын
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
@MilanJovanovicTech2 жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
@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.
@rezarezash2 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
Take a look at the other videos also
@zhivkopetkov41332 жыл бұрын
I found you via LinkedIn, nice videos for sure, keep the good work
@MilanJovanovicTech2 жыл бұрын
Thank you very much. Any content wishes? 😁
@ugochukwuumerie63782 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
That's exactly the idea. I want to show how we can use the Domain Events pattern for offloading certain tasks to the background.
@ugochukwuumerie63782 жыл бұрын
@@MilanJovanovicTech Awesome 👌
@alexwexov42982 жыл бұрын
Great video !
@MilanJovanovicTech2 жыл бұрын
Thanks!
@marhoily2 жыл бұрын
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?
@MilanJovanovicTech2 жыл бұрын
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.
@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 Жыл бұрын
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.
@ПётрЗахаревич-э8и Жыл бұрын
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 Жыл бұрын
You are expressing intent via the method name. In terms of the domain, creating a new invitation instance is "sending the invitation"
@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).
@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 Жыл бұрын
Business logic does belong in the Domain
@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 Жыл бұрын
I like to generate IDs client side when using Guids
@vivekkaushik95082 жыл бұрын
Which keyboard do you use? I really like the sound of it. Blue switches?
@MilanJovanovicTech2 жыл бұрын
Nothing fancy: Logitech K120
@mohamedasem6523 Жыл бұрын
great video, I want to know what's the alternative for returning null, did you make a video for that?
@MilanJovanovicTech Жыл бұрын
Return a Result object
@nouchance2 жыл бұрын
Thank you Sir! Amazing:))
@MilanJovanovicTech2 жыл бұрын
I'm glad you liked it. What is your biggest takeaway from this one?
@wellyngtond22 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
Yup, that's the proper approach. Store it somewhere, and process it later.
@nettoaoquadrado Жыл бұрын
Amazing!!!
@MilanJovanovicTech Жыл бұрын
Thank you! Cheers!
@danilodjokic53032 жыл бұрын
Milan, what is in your opinion the advantage of having a static factory method instead of implementing the full factory pattern ?
@MilanJovanovicTech2 жыл бұрын
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.
@mgame80822 жыл бұрын
Which program are you using for drawing class diagram? 1:10
@MilanJovanovicTech2 жыл бұрын
draw.io
@mahmoudalballah3387 Жыл бұрын
Awesome!
@MilanJovanovicTech Жыл бұрын
Glad you think so!
@athlentral-14912 ай бұрын
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!!!
@MilanJovanovicTech2 ай бұрын
I don't like at the SRP from that perspective when it comes to the domain aggregates.
@Skuerke952 жыл бұрын
For the gathering, you can just use a record. No need for private setters or separate ctors.
@MilanJovanovicTech2 жыл бұрын
But then you end up with an Anemic and Immutable domain model. How will that work?
@haruundk2 жыл бұрын
How would you go about translation to different languages? I mean mostly static error messages etc.
@MilanJovanovicTech2 жыл бұрын
I would leave that for the frontend most likely
@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 Жыл бұрын
Think of the Gathering as a whole. You can't apply single responsibility to absolutely everything out there.
@rezarezash2 жыл бұрын
Hi Milan, Is there a separate video for the Application project?
@MilanJovanovicTech2 жыл бұрын
Closest is CQRS video
@JLPA422 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
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.
@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 Жыл бұрын
Doesn't hundreds of properties make it even messier? We should fix that first
@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?
@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 Жыл бұрын
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.
@MdArifulIslam-ov9yd Жыл бұрын
Awesome ⚡⚡⚡⚡👍👍
@MilanJovanovicTech Жыл бұрын
Thank you! Cheers!
@tplummer217 Жыл бұрын
Excellent
@MilanJovanovicTech Жыл бұрын
Thank you so much 😀
@fxandrei Жыл бұрын
Do you have a link with the book you mentioned about DDD ?
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.
@MilanJovanovicTech2 жыл бұрын
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?
@sajagjain71692 жыл бұрын
@@MilanJovanovicTech I agree with you. Do you think having a convention here might help??
@AlexanderBushuev-p5hАй бұрын
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Ай бұрын
Thanks for the feedback!
@mohammadalisehhat20273 ай бұрын
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?
@MilanJovanovicTech3 ай бұрын
Expose internal members to the unit test assembly
@ghaiathaltrabulsi1352 жыл бұрын
Thanks Milan. Shouldn't the commands and queries live in Domain and theirs handlers live in Application? Is there any wrong with this approach?
@MilanJovanovicTech2 жыл бұрын
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.
@glauberbrennon2 жыл бұрын
@@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
@ravindranathwi2 жыл бұрын
I see return Unit.value where is that. Can't see it in the class anywhere
@MilanJovanovicTech2 жыл бұрын
Coming from MediatR library
@prateekagrawal190729 күн бұрын
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?
@MilanJovanovicTech29 күн бұрын
Why is breaking SRP?
@SoheilKhosroshahi9 ай бұрын
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
@MilanJovanovicTech9 ай бұрын
DDD is about pushing business logic to the domain. So that's the motivation behind the decisions.
@cdsmith85282 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
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?
@cdsmith85282 жыл бұрын
@@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.
@davorzganjer52919 ай бұрын
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?
@davorzganjer52919 ай бұрын
I think I found it kzbin.info/www/bejne/m3SaeIB9frdnfdk
@MilanJovanovicTech9 ай бұрын
There are three options, I talked about them here: kzbin.info/www/bejne/m3SaeIB9frdnfdk
@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 Жыл бұрын
Not in DDD
@rodrigoramirez10312 жыл бұрын
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!
@MilanJovanovicTech2 жыл бұрын
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.
@IAmESG2 жыл бұрын
Now that's the true DDD
@MilanJovanovicTech2 жыл бұрын
🔥
@René-k5iАй бұрын
Hi Milan, you mention a next video about "How to deal with external dependencies", can you add a link?
@MilanJovanovicTechАй бұрын
Possibly this one: kzbin.info/www/bejne/m3SaeIB9frdnfdk
@tanvir.programming2 жыл бұрын
Good video! But one mistake. Keep a private parameterless construct in every entity for serialization and EF query mapping.
@MilanJovanovicTech2 жыл бұрын
Are you sure it's a mistake? Maybe I did it on purpose? 😉
@syedib2 жыл бұрын
I guess Millan will not use EF Dbcontext to query from DB.. instead he will use Raw sql Query for reading data and map them into Custom class
@newraze2 жыл бұрын
@@syedib as i think the reason is that plain sql commands are more accurate and faster than EF for reading data (of course if its written well)?
@adisilagy2 жыл бұрын
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?
@MilanJovanovicTech2 жыл бұрын
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.
@adisilagy2 жыл бұрын
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.
@MilanJovanovicTech2 жыл бұрын
@@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?
@adisilagy2 жыл бұрын
@@MilanJovanovicTech it would be great if you can create one
@adisilagy2 жыл бұрын
@@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
@emmanuelcaulin43957 ай бұрын
Do you use async every time you call db operations even if it only returns 1 row of data or 1 column?
@MilanJovanovicTech7 ай бұрын
Yes
@DeejayWazzouille Жыл бұрын
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 Жыл бұрын
You can create a factory method for that
@MDP13662 жыл бұрын
Hi Milan, Thanks for this awesome video. By the way, I want to mention that your code heavily violates SRP and OCP.
@MilanJovanovicTech2 жыл бұрын
Care to explain why that is? That would be the least you can do when criticizing someone's work 😁
@MDP13662 жыл бұрын
@@MilanJovanovicTech First of all, Big fan, I really like your video. Now as a developer, for example, the Create methods of the Gathering class violates SRP because it does more than one thing and the abstraction level is not the same, also because of the switch case in the method, if you need to add new GatheringType, you need to change the function. It violates OCP. And so on. By the way, I really don't want to criticize anyone, especially those who try to teach something new to others. 🌻
@MilanJovanovicTech2 жыл бұрын
@@MDP1366 I'm always open to a nice discussion! How does it do more than one thing? It just creates a new Gathering. As far as OCP, I agree. And there are ways to go about that. But I don't think it's necessary to dogmatically follow every pattern and principle out there. You have to be pragmatic about it, and not add too much complexity.
@MDP13662 жыл бұрын
@@MilanJovanovicTech I do not agree about the dogmatic part and also OCP/SRP is not every pattern. these are for better codding style but I do agree that simplicity is ultimate satisfaction :) I have a suggestion, if you don't mind, we can arrange a video call or something and work on it together, maybe you changed my mind :)
@glauberbrennon2 жыл бұрын
@@MilanJovanovicTech i understand both points of view. we could change from a big factory method to some factory class that would use that "GatheringType" argument to select which "Gathering" factory method it wants to execute using this approach we wouldnt need to touch the same code everytime we need to add a new "gathering type" preventing us from "changing" old code
@arunbm1237 ай бұрын
very brilliant Video...
@MilanJovanovicTech7 ай бұрын
Many many thanks
@patrykklimas43982 жыл бұрын
What do you think about defining repository interface in the Domain layer?
@MilanJovanovicTech2 жыл бұрын
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
@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 Жыл бұрын
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 Жыл бұрын
@@MilanJovanovicTech humm, got it. Thanks for the answer.
@jacobnunez7678 Жыл бұрын
Hi Milan, could you recommend me some books about clean architecture and cqrs?
@MilanJovanovicTech Жыл бұрын
Clean Architecture by Uncle bob
@jacobnunez7678 Жыл бұрын
@@MilanJovanovicTech Thanks.
@alibabarahaei2229 Жыл бұрын
perfect
@MilanJovanovicTech Жыл бұрын
Thank you!
@microtech244810 ай бұрын
Everything looks good except one thing which is not clear. While you are calling Add methods of invitation and attendee repositories to add the entity and persist using unit of work, why do you need to add those invitation and attendee in gathering entity, what purpose it is solving?
@MilanJovanovicTech10 ай бұрын
Purist approach
@microtech244810 ай бұрын
@@MilanJovanovicTech can you please explain this? Would it be necessary to add invatation and attendee to gathering entity while you have separate repository to persist them?