Log in

goodpods headphones icon

To access all our features

Open the Goodpods app
Close icon
Weekly Dev Tips - Domain Events - After Persistence

Domain Events - After Persistence

06/04/18 • 5 min

Weekly Dev Tips

Domain Events - After Persistence

The previous tip talked about domain events that fire before persistence. This week we'll look at another kind of domain event that should typically only fire after persistence.

Sponsor - DevIQ

Thanks to DevIQ for sponsoring this episode! Check out their list of available courses and how-to videos.

Show Notes / Transcript

If you're new to the domain events pattern, I recommend you listen to episode 22 before this one. In general, I recommend listening to this podcast in order, but I can't force that on you...

When you have a scenario in your application where a requirement is phrased "when X happens, then Y should happen," that's often an indication that using a domain event might be appropriate. If the follow-on behavior has side effects that extend beyond your application, that's often an indication that the event shouldn't occur unless persistence is successful. Let's consider a contrived real-world example.

Imagine you have a simple ecommerce application. People can browse products, add them to a virtual cart or basket, and checkout by providing payment and shipping details. Everything is working fine when you get a new requirement: when the customer checks out, they should get an email confirming their purchase. Sounds like a good candidate for a domain event, right? Ok, so your first pass at this is to simply go into the Checkout method and raise a CartCheckedOut event, which you then handle with a NotifyCustomerOnCheckoutHandler class. You're using a simple in-proc approach to domain events so when you raise an event, all handlers fire immediately before execution resumes. You roll out the change with the next deployment. Unfortunately, another change to the codebase resulted in an undetected error related to saving new orders. Meaning, they're not being saved in the database. Now the result is that customers are checking out, being redirected to a friendly error screen, but also getting an email now confirming their order was placed. They're mostly assuming everything is fine on account of the pleasant email confirmation, but in fact your system has no record of the order they just placed because it didn't save. In this kind of situation, you'd really rather not send that confirmation email until you've successfully saved the new order.

While in-proc domain events are often implemented using simple static method calls to raise or register for events, post-persistence events need to be stored somewhere and only dispatched once persistence has been successful. One approach you can use for this in .NET applications is to store the events in a collection on the entity or aggregate root, and then override the behavior of the Entity Framework DbContext so that it dispatches these events once it has successfully saved the entity or aggregate. My CleanArchitecture sample on GitHub demonstrates how to put this approach into action using a technique Jimmy Bogard wrote about a few years ago. It involves overriding the SaveChanges method on the DbContext, finding all tracked entities with events in their collection, and then dispatching these events. His original approach fires the events before actually saving the entity, but I much prefer persisting first and using a different kind of domain event for immediate, no side effect events.

In the Clean Architecture sample, I have a simple ToDo entity that raises an event when it is marked complete. This event is only fired once the entity's state is saved. At that point, a handler tasked with notifying anybody subscribed to that entity's status could safely send out notifications. The pattern is very effective as a lightweight way to decouple follow-on behavior from actions that trigger them within the domain model, and it doesn't require adding additional architecture in the form of message queues or buses to achieve it.

Would your team or application benefit from an application assessment, highlighting potential problem areas and identifying a path toward better maintainability? Contact me at ardalis.com and let's see how I can help.

Show Resources and Links

plus icon
bookmark

Domain Events - After Persistence

The previous tip talked about domain events that fire before persistence. This week we'll look at another kind of domain event that should typically only fire after persistence.

Sponsor - DevIQ

Thanks to DevIQ for sponsoring this episode! Check out their list of available courses and how-to videos.

Show Notes / Transcript

If you're new to the domain events pattern, I recommend you listen to episode 22 before this one. In general, I recommend listening to this podcast in order, but I can't force that on you...

When you have a scenario in your application where a requirement is phrased "when X happens, then Y should happen," that's often an indication that using a domain event might be appropriate. If the follow-on behavior has side effects that extend beyond your application, that's often an indication that the event shouldn't occur unless persistence is successful. Let's consider a contrived real-world example.

Imagine you have a simple ecommerce application. People can browse products, add them to a virtual cart or basket, and checkout by providing payment and shipping details. Everything is working fine when you get a new requirement: when the customer checks out, they should get an email confirming their purchase. Sounds like a good candidate for a domain event, right? Ok, so your first pass at this is to simply go into the Checkout method and raise a CartCheckedOut event, which you then handle with a NotifyCustomerOnCheckoutHandler class. You're using a simple in-proc approach to domain events so when you raise an event, all handlers fire immediately before execution resumes. You roll out the change with the next deployment. Unfortunately, another change to the codebase resulted in an undetected error related to saving new orders. Meaning, they're not being saved in the database. Now the result is that customers are checking out, being redirected to a friendly error screen, but also getting an email now confirming their order was placed. They're mostly assuming everything is fine on account of the pleasant email confirmation, but in fact your system has no record of the order they just placed because it didn't save. In this kind of situation, you'd really rather not send that confirmation email until you've successfully saved the new order.

While in-proc domain events are often implemented using simple static method calls to raise or register for events, post-persistence events need to be stored somewhere and only dispatched once persistence has been successful. One approach you can use for this in .NET applications is to store the events in a collection on the entity or aggregate root, and then override the behavior of the Entity Framework DbContext so that it dispatches these events once it has successfully saved the entity or aggregate. My CleanArchitecture sample on GitHub demonstrates how to put this approach into action using a technique Jimmy Bogard wrote about a few years ago. It involves overriding the SaveChanges method on the DbContext, finding all tracked entities with events in their collection, and then dispatching these events. His original approach fires the events before actually saving the entity, but I much prefer persisting first and using a different kind of domain event for immediate, no side effect events.

In the Clean Architecture sample, I have a simple ToDo entity that raises an event when it is marked complete. This event is only fired once the entity's state is saved. At that point, a handler tasked with notifying anybody subscribed to that entity's status could safely send out notifications. The pattern is very effective as a lightweight way to decouple follow-on behavior from actions that trigger them within the domain model, and it doesn't require adding additional architecture in the form of message queues or buses to achieve it.

Would your team or application benefit from an application assessment, highlighting potential problem areas and identifying a path toward better maintainability? Contact me at ardalis.com and let's see how I can help.

Show Resources and Links

Previous Episode

undefined - Domain Events - Before Persistence

Domain Events - Before Persistence

Domain Events - Before Peristence

Domain Events are a DDD design pattern that in my experience can really improve the design of complex applications. In this episode I describe specifically how you would benefit from raising and handling these events prior to persisting the state of your entities.

Sponsor - DevIQ

Thanks to DevIQ for sponsoring this episode! Check out their list of available courses and how-to videos.

Show Notes / Transcript

So before we get started, let's describe what a domain event is. A domain event is something that happens in your system that a domain expert cares about. Domain events are part of your domain model. They belong in the Core of your Clean Architecture. They should be designed at the abstraction level of your domain model, and shouldn't reference UI or infrastructure concerns.

Domain events are a pattern, and one with several difference implementation approaches. I generally segment these approaches into two camps: before persistence and after persistence. For this tip, we're going to focus on events that occur and are handled prior to persistence. In future tips, I'll talk about domain events that should only be dispatched once persistence has been successful.

So, as a pattern, what problem are domain events designed to solve? Just as with other event-driven programming models, such as user interface events, domain events provide a way to decouple things that occur in your system from things such occurrences trigger. A common example I use is checking out a shopping cart from an ecommerce site. When the user checks out, a variety of other things generally should take place. The order should be saved. Payment should be processed. Inventory should be checked. Notifications should be sent. Now, you could put all of this logic into a Checkout method, but then that method is going to be pretty large and all-knowing. It's probably going to violate the Single Responsibility and Open/Closed Principles. Another approach would be to raise an event when the user checks out, and then to have separate handlers responsible for payment processing, inventory monitoring, notifications, etc.

Looking specifically at events that make sense to handle before persistence, the primary rule is that such events shouldn't have any side effects external to the application. A common scenario is to perform some kind of validation. Imagine you have a Purchase Order domain object, which includes a collection of Line Item objects. The Purchase Order has a maximum amount associated with it, and the total of all the Line Item object amounts must not exceed this amount. For the sake of simplicity let's say the Purchase Order object includes the logic to check whether its child Line Item objects exceeds its maximum. When a Line Item object is updated, how can we run this code?

One option would be to provide a reference from each Line Item to its parent Purchase Order. This is fairly common but results in circular dependencies since Purchase Order also has a reference to a collection of its Line Item objects. These objects together can be considered an Aggregate, and ideally dependencies should flow from the Aggregate Root (in this case Purchase Order) to its children, and not the other way around. So, let's assume we follow this practice, which means we can simply call a method on Purchase Order from Line Item directly.

Another common approach I see developers use instead of domain events is to pull all of the logic up from child objects into the aggregate root object. So instead of having a property setter or property on Line Item to update its amount, there might be a method on Purchase Order called UpdateLineItemAmount that would do the work. This breaks encapsulation and will cause the root object to become bloated while the child objects become mere DTOs. It can work, but it's not very good from an object-oriented design standpoint.

So how would domain events apply here? First, you'd put the logic to modify Line Item on the Line Item class where it belongs. Then, in the UpdateAmount method, you would raise an appropriate domain event, like LineItemAmountUpdated. The aggregate root would subscribe to this event and would handled the event in process (not asynchronously or in another thread). If necessary, the handler could raise an exception. In any case, it could update properties of the root object to indicate whether it was currently in a valid state, which could easily be reflected in the UI. This is one particular use case for domain events that I've found very helpful, and which I typically refer to as aggregate events since there isn't a sep...

Next Episode

undefined - Do I Need a Repository?

Do I Need a Repository?

Do I Need a Repository?

This week we'll answer this extremely common question about the Repository pattern, and when you should think about using it.

Sponsor - DevIQ

Thanks to DevIQ for sponsoring this episode! Check out their list of available courses and how-to videos.

Show Notes / Transcript

This week we're going to return to the Repository design pattern to answer a very common question: when should you use it? This question appears very frequently in discussions about Entity Framework or EF Core, usually with someone saying "Since EF already acts like a repository, why would you create your own repository pattern on top of it?"

Before we get into the answer to this question, though, let me point out that if you're interested in the repository pattern in general I have a link to a very useful EF Core implementation in the show notes for this episode that should help get you started or perhaps give you some ideas you can use with your existing implementation. Also, just a reminder that we talked about the pattern in episode 18 on query logic encapsulation, but otherwise I haven't spent a lot of time on repository tips here, yet.

Ok, so on to this week's topic. Should you bother using the repository pattern when you're working with EF or EF Core, since these already act like a repository? If you Google for this, you're likely to discover an article discussing this topic that suggests repository isn't useful. In setting the scene, the author discusses an app he inherited that had performance issues caused by lazy loading, which he says "was needed because the application used the repository/unit of work pattern."

Before going further, let's point out two things. One, lazy loading in web applications is evil. Just don't do it except maybe for internal apps that have very few users and very small data sets. Read my article on why, linked from the show notes. Second, no, you don't need lazy loading if you're using repository. You just need to know how to pass query and loading information into the repository.

The author later goes on to say "one of the ideas behind repository is that you might replace EF Core with another database access library but my view it's a misconception because a) it's very hard to replace a database access library, and b) are you really going to?" I agree that it's very hard to replace your data access library, unless you put it behind a good abstraction. As to whether you're going to, that's a tougher one to answer. I've personally seen organizations change data access between raw ADO.NET, Enterprise Application Block, Typed Datasets, LINQ-to-SQL, LLBLGen, NHibernate, EF, and EF Core. I've probably forgotten a couple. Oh yeah, and Dapper and other "micro-ORMs", too. If you're using an abstraction layer, you can swap out these implementation details quickly and easily. You just write a new class that is essentially an adapter of your repository to that particular tool. If you're hardcoded to any one of them, it's going to be a much bigger job (and so, yeah, you're less likely to do it because of the pain involved.)

Next, the author lists some of the bad parts of using repository. First, sorting and filtering, because a particular implementation he found from 2013 only returned an IEnumerable and didn't provide a way to allow filtering and sorting to be done in the database. Yes, poor implementations of a pattern can result in poor performance. Don't do that if performance is important. Next, he hits on lazy loading again. Ironically, at the time this article was published, EF Core didn't even support lazy loading, so this couldn't be a problem with it. Unfortunately, now it does, but as I mentioned, you shouldn't use it in web apps anyway. It has nothing to do with repository, despite the author thinking they're linked somehow. His third perf-related issue is with updates, claiming that a repository around EF Core would require saving every property, not just those that have changed. This is also untrue. You can use EF Core's change tracking capability with and through a repository just fine.

His fourth and final "bad part" of repositories when used with EF Core is that they're too generic. You can write one generic repository and then use that or subtype from it. He notes that it should minimize the code you need to write, but in his experience as things grow in complexity you end up writing more and more code in the individual repositories. Having less code to write and maintain really is a good thing. The issue with complexity resulting in more and more code in repositories is a symptom of not using another...

Episode Comments

Generate a badge

Get a badge for your website that links back to this episode

Select type & size
Open dropdown icon
share badge image

<a href="https://goodpods.com/podcasts/weekly-dev-tips-155124/domain-events-after-persistence-8360130"> <img src="https://storage.googleapis.com/goodpods-images-bucket/badges/generic-badge-1.svg" alt="listen to domain events - after persistence on goodpods" style="width: 225px" /> </a>

Copy