Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event Sourcing - where does Domain Logic fit in?

I've just been watching Greg Youngs talk on Event Sourcing, but i'm confused as to where business logic fits in. A simple example:

1) Shopping Cart Created 
2) Item Added
3) Item Added
4) Promotional Code - 20% Off

Is the Promotional Code calculated against the shopping carts items and the result of that stored as an event. I understand "PromotionalCodeAddedEvent" would maybe make sense, but where does the math happen? I'm thinking:

public void AddPromotionalCode(PromotionalCode code)
{
    //perform calculation against shopping cart items. 
    //if valid
    ApplyChanges(cmd);
}

Then the result doesn't end up anywhere and the Read Model would have to perform the calculations.

I'm not fully understanding the concept, any help would be great.

like image 671
TBD Avatar asked Jan 12 '17 11:01

TBD


1 Answers

Something as seemingly simple as a promotional code can actually be a fairly complex use case. Primarily, this is due to the fact that a promotional code is logic that (usually) is maintained by one or more business users, whereas it also belongs inside of the domain, itself. It is non-traditional, in that sense. There are multiple ways to handle this, and what I will outline would just be my personal approach.

For the sake of argument, assume that you have a simple series of known promotional codes, such as:

  • X% Off Purchase, with or without a Minimum Purchase
  • $X Off Purchase, with or without a Minimum Purchase

We can make some assumptions, as well:

  • A Promotional Code has a Start Date
  • A Promotional Code has an End Date

The application of a promotional code can be tricky. Consider the two scenarios that we have defined. "$X Off Purchase" is comparatively simple, because it is a fixed amount. "X% Off Purchase", however, is more complex. If we only had the fixed amount, we could apply the discount to the cart as soon as any thresholds are met. With a percentage-based discount, if the user were to add two items, add a promotional code, and then add another item, the promotion would have already been "applied".

Because of this, I would personally only "attach" a promotional code to a cart. Why? The reason is that at the point of checkout, I can likely assume that a cart is going to be used to generate an order. Until that time, the cart's contents are fluid. A user's action against the cart will change the total value of the cart, as well as the total discount, assuming a non-fixed discount amount. It can also render either discount as being invalid, should a user remove one or more items from a cart and the total value of the cart fall below the threshold to apply the discount.

So, I would actually have multiple commands which would would be involved. Essentially any commands that impact the value of the cart could change the discount amount. To that end, I would be looking for the discount amount to be recalculated for the commands which:

  • Add an item to the cart
  • Remove an item from the cart
  • Change the quantity of items in the cart
  • Add a promotional code to the cart
  • Change the promotional code attached to the cart

Since these all are operations against a cart, I would be calculating the discount within the promotional code, itself, with the participation of the data contained in the cart. It feels like a promotional code is going to be an aggregate, going down this path. So, I would have the command handlers invoke a domain service that can provide my cart with the information that it requires. That domain service is going to load the promotional code, and I am going to be able to pass in the line items within that cart, so that the promotional code will tell me what the calculated discount would be. Then, I am going to generate the event, which contains the new value of the cart, along with the adjusted value (the discount). Going down this path, the logic for calculating a discount based upon line items within a cart is the responsibility of the promotional code.

You could put this responsibility in the cart, instead. Personally, though, I feel as if encapsulation of domain logic within the promotional code, itself, makes more sense. I had mentioned that it is likely that you will generate an order from a cart. By having the promotional code as an aggregate, and it containing the logic to apply the discount based upon the line items, we have a single truth, in how we calculate a discount for line items - whether that is in terms of a cart or in terms of an order.

like image 155
Joseph Ferris Avatar answered Oct 23 '22 04:10

Joseph Ferris