Service Boundaries in a domain driven design and Leaky boundaries — Part 1
In one of my previous articles where I discussed about clean architecture(https://medium.com/@surajit.das0320/understanding-clean-architecture-in-python-deep-dive-on-the-code-17141dc5761a), one of the major concerns is about service boundaries. Let’s take an example of an eccommerce application. We have a payment and order service.
Payment service — This service is solely responsible for processing payments for a customer.
Order service — This service is solely responsible for handling order processing and fulfilment.
Let’s think of a scenario where we have a customer who places an order and makes a purchase from our website. So, we have the customer who browses the site, adds an item to the cart and then proceeds to checkout and purchases the product. When the payment is made, we would like to change the status of the order from pending to processing. So, what we can see here is that when the customer makes the payment, the payment service is requried to notify the order service to mark the order status from pending to processing.
We are more interested in how these two services communicate with each other without necessarily creating any sort of coupling and making the design more loosely coupled.
Conceptual design — In this article, I want to discuss the conceptual understanding behind the design and then in the next article, I will go through the actual implementation of the code.
So, we are more interested in the communication between these two services. We will use the Event driven design to implement this functionality.
Customer journey from checkout flow
- The customer is presented the checkout page. Let’s assume we will use the Stripe checkout session to handle the payments.
- The stripe checkout page is rendered and the customer enters the card details for payment.
- The customer then makes the payment and stripe successfully processes the payment.
- Stripe will then redirect the user to the success_url that we configured.
- Behind the scenes, Stripe will post to our configured web-hook about the payment_intent and other charge details which we can save in our database if we need. But that’s a tangent for now. Let’s not focus on the stripe flow for now.
- Once, we successfully received the stripe payment detail response in our web-hook, we will want to notify the order service that the payment was successful for this customer.
- Here is the meaty part. We will use Kafka for our pub-sub design and let’s say we have an event called “payment_successful” . The order service will subscribe to this event. So, when the payment is successful, the payment service will publish this event with a payload. e.g
{
"event_type": "payment_successful",
"event_timestamp": "2023-11-06T14:30:00",
"order_id": "12345",
"customer_id": "789",
"payment_amount": 100.00,
"payment_method": "Credit Card",
"transaction_id": "ABC123XYZ",
"currency": "USD",
"payment_status": "Success",
}
The order service will then consume this message and then mark the order_status as processing which will then move the order state to fulfilment stage.
Through this design, we have created a very loosely couple form of communication between two services. In the next article, I will show an actual implementation of the code so that we can go through how the message is produced and added to the message bus and how the consumer on the order service processes the message and offloads it to the appropriate handler.