Domain Events | Clean Architecture & Domain-Driven Design from scratch | Part 17

Ғылым және технология

Get the source code: / amantinband .
In today's video, we'll implement Domain Events from Scratch.
We'll see why domain events are such a fundamental part of Domain-Driven Design and how we can use domain events to "communicate" between aggregates.
Link to the full playlist: • ASP.NET 6 REST API Fol...
Connect with me on 'em socials:
Twitter: / amantinband
LinkedIn: / amantinband
GitHub: github.com/amantinband

Пікірлер: 89

  • @pedrofreitaslima
    @pedrofreitaslima Жыл бұрын

    Thank you Ami, i watch all videos in playlist "REST API following CLEAN ARCHITECTURE & DDD Tutorial" and help me understanding the Clean Architecture.I really liked your teaching, and like you use the most relevant and newest package in this lessons, such as Mapsters, MediatR, FluenValidations, EntityFramework. I will use this learnings to develpoment my capstone project.

  • @z_prospective160
    @z_prospective160 Жыл бұрын

    Very cool! I like it! The overhead of the structure is worth it.. and hopefully you are able to put this boilerplate in a common library you can reference from a nuget feed. I never thought to publish the domain events from persistence layer as a part of save changes. I have done things like modify the entity's audit fields in the save changes method before. This is very cool and I'll be using this for sure. thank you!

  • @samehhakim4340
    @samehhakim4340 Жыл бұрын

    👍👍👍👍👍 Thank you, your explanation is really very simple

  • @alan-
    @alan- Жыл бұрын

    I really do appreciate the content, but following along with this is made unnecessarily difficult by there being many hidden changes in the underlying code from episode to episode, meaning that it is impossible to follow without being a member on patreon and even with that, it is very awkward and off-putting. It would really help if you could put an on-screen summary sheet of what or where the changes are, so we don't bump into them and then have to piece together what the differences are from the patreon source code in order for the code to run. I am very grateful for the content, it is great to have such in depth detail on a complex topic, but it would be easier to follow along if there was a brief summary sheet at least outlining where the additional changes are. The most significant changes were when many of the IDs changed from guids to strings between one set of episodes, then in a later episode, they had changed back to guids again.

  • @LoZioIAR
    @LoZioIAR Жыл бұрын

    Thank you as usual!! Should be very good if you'll add a windows that show what key are you writing!

  • @weilei888
    @weilei888 Жыл бұрын

    wow.great video..

  • @issacproton7885
    @issacproton78857 ай бұрын

    very insightful!

  • @issacproton7885
    @issacproton78857 ай бұрын

    I loved it!

  • @sneigee
    @sneigee Жыл бұрын

    that awesome. thanks for this. and please try and release it early. Stay bless bro

  • @salarrabbal4059
    @salarrabbal40594 ай бұрын

    Thank you for providing such a comprehensive explanation. Over the years, I've encountered various implementations of this concept, yet many seem detached from practical application in the real world. For instance, in your approach, what would happen if domain events were cascaded, or situations arose where the use of IDbContextFactory to handle aggregates in batches precluded injecting the current instance of your Repository in event handlers? In my experience, addressing these challenges often leads to encountering significant accidental complexities, which may prompt a shift towards embracing eventual consistency over immediate event publication prior to saving changes. Once more, I value the insights shared in your informative videos.

  • @reshiefaisal1477
    @reshiefaisal1477 Жыл бұрын

    Hey man it's a great help that you're making these videos. These videos have helped. I kindly request to make a video on Test Integrations as well. That will be very kind full of yours.

  • @amantinband

    @amantinband

    Жыл бұрын

    I have a whole sub series planned on testing. This will include integration tests as well

  • @reshiefaisal1477

    @reshiefaisal1477

    Жыл бұрын

    Thank you. That will appreciated.

  • @evgeniysir4220
    @evgeniysir4220 Жыл бұрын

    Great video! Thank you! Maybe not exactly on the topic of the video, but I have a problem that I don't know how to solve yet using the existing architecture. Namely: I add a function to the repository to search for an entity by identifier, I pass an identifier object to the function as a parameter (HostId hostId hostId as an example), inside the function I use FindAsync(hostId). When executed, the exception "System.ArgumentException: The key value at position 0 of the call to 'DbSet.Find' was of type 'HostId', which does not match the property type of 'AggregateRootId'" occurs. Considering that AggregateRootId is an abstract class, how to solve this problem? I would be grateful for a hint.

  • @ferventurart
    @ferventurart Жыл бұрын

    Great work Amichai!! But whats happen if you have identity Id. For example, if we follow this tutorial we always created at entity with Id 0, and if on our domain event want insert on another table it throws error.

  • @juliangzr4998
    @juliangzr499811 ай бұрын

    Hello! thank you for your video. I have a question: How can i do to throw domain events when i delete an entity? in that case it's not possible to save the domain event inside the entity because you are deleting it. I usually delete the entity through the repository.

  • @avi7278
    @avi72786 ай бұрын

    what is the app that you're using for presentation in your video?

  • @indeem1507
    @indeem1507 Жыл бұрын

    I really liked how you explained and showed how to implement domain events. Whats kind of missing for me is, why? Because you did not implement the notification Handler i could not really see how domain events are useful.

  • @irfanshaik1302

    @irfanshaik1302

    2 ай бұрын

    From what I understood, the handler will contain the logic of adding the menu id to particular dinner(using dbcontext) as mentioned in the beginning of the video. So simply put, handler also contains db operations, which are outside the scope of the aggregate, but these all operations will run at once because we are calling these handlers before save changes, and they should run at once because they are related operations.

  • @DrHeinzDoofenshmirtz
    @DrHeinzDoofenshmirtz Жыл бұрын

    Great! I would really have liked to hear you talk about (maybe with an example) how other entities would react to the domain event. For example, as you are talking about the two-way reference issue. Would the Menu be created and store a reference to the DinnerId, and when the event is triggered, a specific event handler would take the dinner id, fetch the dinner from the database, and then update its reference to the Menu? Or I guess it is the other way around: When the Dinner is created and a DinnerCreated event is fired, the Menu would add it to its inner list of dinners?

  • @amantinband

    @amantinband

    Жыл бұрын

    Thanks! Yup the second one. The event handler would sit under the Menus folder in the application layer (FYI for Patrons: the Patreon source code includes this exact example)

  • @DrHeinzDoofenshmirtz

    @DrHeinzDoofenshmirtz

    Жыл бұрын

    @@amantinband That is a very good reason to become a Patreon 😊

  • @zero8_8
    @zero8_8Ай бұрын

    Good afternoon! I really liked the course! Is it possible to find out if there is a sequel? I would like to go through the following stages, where all the functionality of the application will be analyzed in detail and the final architecture will be shown. I am especially interested in whether not only authorization and authentication will be covered, but also other important functions.

  • @alexanlp
    @alexanlp10 ай бұрын

    Sorry for my question, bur which font you're using in VS Code?

  • @user-wj4rj2ip7e
    @user-wj4rj2ip7e Жыл бұрын

    Hello! I've been racking my brain with this. I have been following along all of your videos and understand the concept, but I'm hitting a roadblock when creating the migration, where it is creating a shadow property on a foreign key. This would be the equivalent in your example between dinner and reservations.

  • @batiku1
    @batiku19 ай бұрын

    Why can't we pubish the domain event(s) right from the domain layer?

  • @mohamedal-qadeery6530
    @mohamedal-qadeery6530 Жыл бұрын

    Hello i want to start this playlist how many videos are remaing for this to finish ? this the best content on youtube !

  • @patrykk.4630
    @patrykk.4630 Жыл бұрын

    Hi, I am back after a long pause. Yesterday I made changes to my aggregate roots (identity paradox) and my mapper stopped working for AuthenticationResult -> AuthenticationResponse. It says 'Cannot convert immutable types, please consider using MapWith'.

  • @verified_tinker1818
    @verified_tinker18183 ай бұрын

    What software are you using in these videos?

  • @jamesroot9777
    @jamesroot9777 Жыл бұрын

    Hey Amichai, absolutely love the videos. Have followed from part 1, and watched all the videos a few times already. I'm in a course right now, and we are tasked with wiriting a simple Forum RESTful API, but I got invested into what you are doing and wanted to adapt it. I've since hit a roadblock. Could you give me some advice on how I can scale down the project so it makes sense for someone whose closer to a beginner like myself? Would the User, Post and Comment aggregates need to follow what you've done so far, including the AggregateRoot, Entity and Value Object models? How do I handle roles, as in "Admin" and "Basic User"? What do you think I can ignore from your DDD, keeping in mind my project is extremely small compared to what you're doing, and we are not expected to have even half of the features and implementations you're using. (Although, I'd love to keep all of 'em, favourite is ErroOr). Keep the videos coming!

  • @mohamedal-qadeery6530

    @mohamedal-qadeery6530

    Жыл бұрын

    im a beginner too but i think its going to be something similar to the guest/host aggregates where u would have value object of userId

  • @SureshKumar-rz7dn
    @SureshKumar-rz7dn Жыл бұрын

    Great Video as always :-) Can we use this pattern for sending events(integration event) externally to a service bus queue?

  • @ZeBobo5

    @ZeBobo5

    Жыл бұрын

    You can add bus publishing in a domain event handler. And why not inside another assembly

  • @kmcdo
    @kmcdo Жыл бұрын

    public record TreeFell(bool madeSound) : IDomainEvent; If there's no handlers, did the event even happen? ;-)

  • @amantinband

    @amantinband

    Жыл бұрын

    HAHAHAHA Dad jokes 💪🏼

  • @parthadutta1168
    @parthadutta11685 ай бұрын

    How to implement domain events when domain entities and persistence entities are different, i.e. domain entities are persistence ignorant?

  • @wilfriedkinda
    @wilfriedkinda Жыл бұрын

    Hi Amichai very good and informative video as always just need an open mind for me who uses mongoDb as a database how to set up an interceptor!? or what logic I can use I'm a beginner.

  • @TheFeljoy

    @TheFeljoy

    Жыл бұрын

    An interceptor is just a nice way of adding this logic to the save method of entity framework. The simplest way would be to create a new method that calls whatever save method you use for mongodb inside it and calls publish domain events just before that. Then you always use your custom save method instead of the default one. For bigger projects with many contributors it’s important to build things in a way where you couldn’t do things the wrong way anymore (because it’s not possible) but for your own project this is much less important.

  • @AlexGait
    @AlexGait Жыл бұрын

    Hey, Amichai! Great video as always! Is there a reason you prefer to have Domain Events in the Entities and not the Aggregates? Will an Entity ever raise a Domain Event? Also, when do you think the testing videos will be released?

  • @allannielsen4752

    @allannielsen4752

    Жыл бұрын

    I too was wondering why they were not in the aggregate. The other thing I have always been told was that domain events exist inside their bounds and integration events cross bounds. It might just be semantics, but I find that coupling using this method leads to the same spaghetti this is supposed to "fix". Furthermore, I was surprised to see you adding the menu instance into the event and not just the ID, could you explain that further too please, as now you are exposing the menu domain object to outside too.

  • @amantinband

    @amantinband

    Жыл бұрын

    Thanks, Alexandros! Both approaches are very common. I've used both, and the main difference is whether the inner entities need to communicate back to the aggregate root that something happened. The main benefit of domain events in the aggregate root is a single, organized place for all events. Since we are organizing our domain layer by feature, it is clear which domain events are associated with which aggregate, which is why I prefer the Entity approach. Does that make sense?

  • @amantinband

    @amantinband

    Жыл бұрын

    @@allannielsen4752 Hey Allan. Domain Events capture an event that happened within a bounded context and are used to implement side effects on other aggregates within the same bounded context. In our application, BuberDinner *is* the bounded context and the side effects are updating zero or more aggregates within BuberDinner. Regarding event parameters - it all depends on your use case. You can pass as little or as much as you need.

  • @AlexGait

    @AlexGait

    Жыл бұрын

    @@amantinband I am not sure I see the benefit, but I guess it is more of a preference. Thanks!

  • @pilotboba

    @pilotboba

    Жыл бұрын

    @@AlexGait The number one benefit I see from using domain events is that it helps with single responsibility and open/closed. (probably the two most important SOLID factors). Right now, we have 1 thing that has to happen after a menu is created. If that's in the create menu handler, fine. But down the road a second thing needs to happen when a menu is created. So, now you have to edit the handler and add code to it. So, that handler has multiple reasons to change now. With domain events, you would simply add a second handler for that event. The more your code never needs to be modified to add features and just added, the better. You're less likely to break something that is already working. Anyway, this is just my opinion. Take it for what you paid for it. :)

  • @issacproton7885
    @issacproton78857 ай бұрын

    would you like to share how do you use Tools like a pro, it feels like Vim on VS Code as well as Visual Studio when you use it.

  • @alexlo5655
    @alexlo5655 Жыл бұрын

    Hey, Amichai! Great video as always!, Could you plz make the "code" screen(s) larger? I can hardly see your code.

  • @amantinband

    @amantinband

    Жыл бұрын

    Thanks! Curious if others want this as well. If yes I’ll bump the font size. thanks for the feedback, Alex 🙏🏼

  • @pilotboba

    @pilotboba

    Жыл бұрын

    @@amantinband It seems fine to me, and I can zoom my screen if needed.

  • @Steven-hq6df
    @Steven-hq6df2 ай бұрын

    I'd be interested in an implementation without depending on ef core

  • @jfeedass
    @jfeedass Жыл бұрын

    Ačiū!

  • @amantinband

    @amantinband

    Жыл бұрын

    Thanks! 🫶🏼

  • @mikefellowes8910
    @mikefellowes89106 ай бұрын

    What is your opinion on request handlers orchestrating changes to multiple aggregates directly, rather than using domain events to orchestrate that logic? Using domain events exclusively to communicate between aggregates could make understanding the flow of a particular use case challenging, couldn't it? If the use case logic is clear then it seems to me that using one handler would be the best approach. I can see that using domain events is flexibile, along with the advantages of the pub-sub nature of INotification, but do you think there is a place for both approaches?

  • @amantinband

    @amantinband

    6 ай бұрын

    Completely agree. That's why I have the following section in Clean Architecture template on GitHub: "Note: Eventual consistency and the domain events pattern add a layer of complexity. If you don't need it, don't use it. If you need it, make sure your system is designed properly and that you have the right tools to manage failures."

  • @mikefellowes8910

    @mikefellowes8910

    5 ай бұрын

    Thanks for the reply @@amantinband - fantastic videos btw, you have a new subscriber! For anyone interested, I came across this Microsoft article making the same argument for domain events. Since I cant paste the link here, do a web search for the section in the article titled: "Domain events as a preferred way to trigger side effects across multiple aggregates within the same domain".

  • @novelhawk
    @novelhawk Жыл бұрын

    Way cleaner approch on the Publish logic in my opinion is the following: - Remove Clean method and instead create a Drain method that cleans and returns what it has cleaned - Replace linq with var events = dbContext.{...} .SelectMany(entry => entry.Entity.DrainDomainEvents()) .ToArray(); // publish events

  • @amantinband

    @amantinband

    Жыл бұрын

    I've taken a similar approach in some projects. If you don't need to read the events (without "popping" them), what you suggested is a safer approach, IMO.

  • @pilotboba

    @pilotboba

    Жыл бұрын

    @@amantinband So the domain events could be a stack instead of a list?

  • @vincentcifello4435
    @vincentcifello4435 Жыл бұрын

    1)"Aggregates are transactional boundaries" Menu and Dinner are separate aggregates. Therefore, they are separate transactional boundaries, by definition. Yet, they are being saved within one database transaction. This is self-contradictory. It is also a seed of destruction.. 2)"Other solutions require a lot of ceremony" This entire video substitutes an elaborate, in process, technical solution, ultimately described as "a lot of boilerplate code", in order to avoid a minimal transaction script that is exactly the same, except that it is explicit. Dinner.Add(myMenu) Menu.Dinners.Add(myDinnerId) ----this one line is the reason for this video dbContext.SaveChanges() 3)Dinner aggregate holds a MenuId That is all that is needed for the relationship between Dinner and Menu. Having a Menu aggregate holding a list of DinnerIds duplicates data. This appears to be the reason that both of these aggregates are being saved in one transaction. Seed.Of.Destruction. I am criticizing the way DDD gets implemented, not Amichai. It is always a convoluted, self-contradictory mess.

  • @TheOneAnOnlyGuy
    @TheOneAnOnlyGuy8 ай бұрын

    How do you justify adding MediatR to the domain? That is a dependency that does not belong in the domain, imo.

  • @Marfig

    @Marfig

    8 ай бұрын

    There is nothing wrong about adding external library dependencies to the domain layer. Even more so if they are utilitarian libraries like MediatR. One common misconception is reading too literally the ol' adage "the domain layer shouldn't depend on anything". What that should read is, the domain layer shouldn't depend on any other structural layer of your project. It's well within DDD best practices to support your domain layer with external libraries that don't create those dependencies, like MediatR for Domain event support. Another example are discriminated union libraries to reduce exception handling in your code.

  • @marcinz9379
    @marcinz9379 Жыл бұрын

    Hey, I have question about DDD. For example we have aggregate Blog, and this Blog has many Posts, and any Posts has many Comments. How to add new Comment to Post? Blog is my AggregateRoot. And now i have problem. I don't want to get from db my whole Blog with all Posts and nested Comments. It's not efficient and also I don't have any aggregate rules for Comments. For example we have: AddCommentCommand(PostId PostId). Can I simply get this Post from db, create new Comment object and save this to db? Or it all should be under our Blog aggregate?

  • @stunna4498

    @stunna4498

    Жыл бұрын

    in this case comment should be an aggrregate itself and you would have a reference to the Post by using the PostId. Then when you query the post you would go to the comment repository and get comments by id or whatever.

  • @amantinband

    @amantinband

    Жыл бұрын

    Check out my videos on domain modeling. As Joel suggested, You would probably want the Post and/or the Comment to be an aggregate root as well (one of the reasons we prefer more aggregates is not having to read big aggregates when we need to make an update to a nested entity)

  • @marcinz9379

    @marcinz9379

    Жыл бұрын

    Thank you guys

  • @nicolasundiano8406

    @nicolasundiano8406

    Жыл бұрын

    ​@@amantinband but what happens if we want to query related data of the Post Aggregate, for example the list of Comments Aggregate included. Do we have to do multiples queries to the db and put them in a PostWithCommentsResponse?

  • @pilotboba

    @pilotboba

    Жыл бұрын

    @@nicolasundiano8406 Why are you querying it? For your query side or for your command side? If for the query side, you can create a read model that just does a simple query on the db and doesn't use the aggregates at all.

  • @modgenesis3868
    @modgenesis3868 Жыл бұрын

    why would you have domain events in the domain objects, the logic could also be handled in the command handlers right? The thing about this approach is that code gets executed at place A but then somewhere in the app it gets fired in another part, this will be hard to debug in the future. But Thanks for this video man!

  • @stunna4498

    @stunna4498

    Жыл бұрын

    your domain events will be called in the domain objects itself. but the code that handles that event will be in the application layer so it won't be hard to debug since you will allways know where that event is handled

  • @amantinband

    @amantinband

    Жыл бұрын

    Yes. The 2 main approaches are orchestrating all the changes from the handler and the domain events pattern I demonstrated on this video. Which is better? That depends on your preference, app, and goals.

  • @pilotboba

    @pilotboba

    Жыл бұрын

    See my reply above. The reasons are to keep your code more solid by following the single responsibility property and the open/closed principle.

  • @zakustolondoo
    @zakustolondoo4 ай бұрын

    Hi Amichai, How can I get the source code as a member?

  • @amantinband

    @amantinband

    4 ай бұрын

    Hey man! It’s a perk for patrons. Being a member gives you only early access to videos as I upload them

  • @zakustolondoo

    @zakustolondoo

    4 ай бұрын

    @amantinband Apologies if I joined the wrong group. I joined because I need access to the source code. How can I transfer my subscription to patron without having to pay again?

  • @amantinband

    @amantinband

    4 ай бұрын

    Moving the subscription isn’t possible. I think you can cancel the membership and you won’t be charged (or maybe you’ll be charged only for the day you were a member)

  • @PelFox
    @PelFox Жыл бұрын

    There's still a problem with consistency. Events could be published and then SaveChanges fails or the SaveChanges fails and some process within an event handler fails. Everything also runs in process, so need to keep that in mind if you have users waiting for a call.

  • @amantinband

    @amantinband

    Жыл бұрын

    Note that all handlers and inner “save changes” are part of the original transaction. If any fail, all changes are rolled back. As long as all side effects are on aggregates within the system, this solution resilient to failures

  • @PelFox

    @PelFox

    Жыл бұрын

    @@amantinband Yes, if they all act within the same transaction and database. What I often see is domain eventhandlers publishing integration events, those could fail and that would result in state changes without messages being published.

  • @amantinband

    @amantinband

    Жыл бұрын

    Exactly. I talk about this somewhere in the video

  • @ANTONZUBAREV
    @ANTONZUBAREV7 ай бұрын

    Отлично! Но слишком сложно/ Great! But it is a too diffcult

  • @timur2887
    @timur28873 ай бұрын

    Don't materialize enumerable twice or more in one function

Келесі