Defining service boundaries is hard. Let's bring some colors and make it fun!
I’ve heard about this technique for the first time from Udi Dahan, but unfortunately it seems no paper is to be found on the topic. This is my humble attempt.
Trying to define services first and partition their functionality later is often a misleading approach ending up in the Entity Services antipattern. One can't really know what the right boundary is up-front, it is a process of discovery.
First, let's define the requirements.
- User finds products
- Title, description, price and in stock availability is shown
- User finds products by category
- User adds a product into cart
- User can add a product into cart multiple times which increases the quantity
- A product already added into cart must not change during the session
- All attributes remains the same, price included
- User removes a single product from the cart
- User empties the entire cart
- User places an order from the cart
- An order can contain additional charges
- User requests a delivery by filling in his name and shipping address
- Payment is requested after the order was placed
- Goods is fetched after the order was placed
- Delivery is accepted after the order was placed
- Delivery is dispatched after was accepted, payment collected and goods fetched
Now, we can analyze those and scratch the first services.
- Find products
- Product ID
That's pretty obvious. The service needs to have the product title, description and price.
- Find products by category
In order to provide this, a list of categories must be shown first:
- List categories
- Category ID
For categories, we probably want some nice looking URIs in our e-shop like /toys or /books. The URI will be used for searching. As we neither want to have URI duplicated in products nor to do two hops to get the Category ID first, we put the List categories use-case into the same service as both Find products use-cases. Let's call it “Blue“:
Next is the product availability. We want to show how many is left in stock. This information is not necessary for sale. A customer can order a product even when there are no items left in stock. This can just delay delivery or, alternatively, another product can be offered. No items in stock must not prevent the customer from placing an order - this is a business decision. This means we can model the stock information in a separate service, let's call it “Green“:
There are two important rules for the shopping cart. First says that a product can be added multiple times. This means we need to hold a quantity with each cart item. Second say that the product in cart must not change. This means the cart items are kinda snapshots of the products at time of adding. That's why putting the cart functionality into the blue service wouldn't be the best idea: we need a new representation of product - a cart item. Besides, cart items are fully independent on products and categories. Let’s put it into a new “Red” service:
Order items have similar rules like cart items, both are immutable snapshots of products at particular time, except we don't need all information about the product in the order. The life cycle differs: while cart items can be added and removed, the order is typically immutable. The order total amount is not only sum of item prices but can include additions like shipping fees, taxes, discounts etc. That's why we need to model the total amount separately. As we agreed the order didn't belong to the same service as shopping cart, we have to put it into a new service, called “Yellow“:
Although the customer fills up shipping information as a part of the order process, the order process is fully independent on it. An order can be placed even without shipping information - consider an online delivery of an e-book etc. We put it into a new service, let's call it “Violet“:
Similar with payment. Here the amount to pay is needed, because the payment doesn't necessarily have to be for the order as whole. Finishing the order process can contain several other payments like additional fees or discounts. A payment is not necessarily connected to an order as it could be used for different purposes like loyalty program, membership fees etc later. Let's call the new service “Orange“:
Fetching goods should have an obvious impact: the amount in stock decreases. We put this use-case into the Green service:
When all the use-cases are partitioned into services, it’s pretty straightforward to give them appropriate names.
You can see this idea in action on my Github.