Introducing Event-Driven Refactor0:00
Hey there, and welcome back. Although we've managed to define our boundaries and build stable APIs for modules to talk to one another, we still have a high degree of coupling. For example, in our PurchaseItems class, the class still knows which modules to talk to. That is, it knows certain modules in certain classes exist. And we can use an event-driven approach to decrease the coupling between those components. This lesson will be divided in two parts. On the first one, we're going to take a quick look at what we have built and what differences would have to happen for us to be able to use that event-driven approach. And on the second half, we're going to implement those.
Reviewing Current Checkout Flow0:35
would have to happen for us to be able to use that event-driven approach. And on the second half, we're going to implement those. As we move through this lesson, we're also going to talk about benefits and downsides of taking this approach. So let's get started. So this is what we started with. We had a request going into the ChancodeController, and then we were doing each step sequentially. We would map the cart items, we would charge the cart, we would create an order, create a payment, and finally start shipment for that order. And all this code lived together.
a payment, and finally start shipment for that order. And all this code lived together. We managed to refactor this, and this is what we ended up with. If we zoom out a little bit, we can get a better view of the process. So things are still happening sequentially, but we have well-defined APIs within our modules to communicate with. As we can see, the PurchaseItems class still has knowledge of the CreatePayment class, which lives in the Payment module. It has a direct reference to it. It doesn't know, however, how to decrease a product stock or how to send an order confirmation.
Publishing Events to Decouple1:33
It has a direct reference to it. It doesn't know, however, how to decrease a product stock or how to send an order confirmation email that is decoupled from the PurchaseItems class. And the way that happens is by publishing an event. This event is what allows those components to be decoupled. It contains all the information necessary for other components to react to it, and the publisher does not know about them. Let's see how we could refactor our checkout process to also follow an event-driven approach. We would still have a request going into a controller, and that controller would still invoke an action, a command, that is, PurchaseItems.
Designing Event-Based Checkout2:05
We would still have a request going into a controller, and that controller would still invoke an action, a command, that is, PurchaseItems. We could then initiate an order, and then instead of also handling the payment, we could just publish an event saying that an order was initiated. And then we could have another component react to this event that was published and try to execute a payment for the order. And then two things could happen. The payment could succeed, in which case we would need an event to complete the order and to also start shipment, maybe send some emails, but the payment could also fail. In that case, we would have to cancel the order.
and to also start shipment, maybe send some emails, but the payment could also fail. In that case, we would have to cancel the order. So as you can see, with an event-driven approach, you have very small components that react to events. Your system becomes reactive. This becomes very easy to test, as well as the other subscribers. So at this point in time, all that we need to test is that the Order is initiated, a record is inserted into the database, and that an event is published. At this point in time, all that we have to test is that a payment for an existing Order is triggered.
Benefits and Downsides3:08
At this point in time, all that we have to test is that a payment for an existing order is triggered. So as you can see, the big difference here is instead of having a big class that handles an entire feature, you divide that into smaller components that react to events being published. That has different benefits. First, you decrease coupling between components, since the component that publishes something does not know who's listening to it. So when the event OrderInitiated is published, the publisher does not know that PayOrder is going to react to it. In the same manner, when PayOrder publishes that a payment succeeded, it does not know
is going to react to it. In the same manner, when payOrder publishes that a payment succeeded, it does not know which listeners are going to react to it. That also gives you some fault tolerance, since each component now does a really small job, and if something breaks, you can replay an event, and obviously it makes it easier to test them. The downside is you're adding complexity, and it becomes harder to understand how a feature works in its entirety. For example, previously you would have a class that handled the process of checking out an order, and now you have that spread across many subscribers, many classes that do one.
Required State Model Changes4:07
For example, previously you would have a class that handled the process of checking out an order, and now you have that spread across many subscribers, many classes that do one specific thing. For our system to be event-driven, there are other things we have to pay attention to. For example, this action would have to change the order state to something like pending. That way our system knows this is not complete. So when payOrder is executed, it is going to look for that pending state. This action would also save a payment with different states. It can succeed, and it can also fail. The same thing would happen here.
It can succeed, and it can also fail. The same thing would happen here. Once a payment succeeds, we need to update the order status. We need the system to know it has been completed, and obviously if the payment fails, we want to either cancel the order or retry it. So as you can see, there are benefits and downsides, as usual, with any approach in software engineering. And on the next lesson, we're going to see how to prepare our application to handle those changes and how to implement those events. See you then.
changes and how to implement those events. See you then.
