Extracting the Delivery Service - Step 1, Split the code
The previous post described the existing monolithic architecture. The first step is to split the code and convert delivery management into a separate, loosely coupled module within the monolith.
The following diagram shows the new structure:
Order Management
invokes Delivery Management
via coarse-grained API to schedule, reschedule, and cancel deliveries.
At this stage, however, the Delivery Management
shares the same database tables used by Order Management
.
For example, both the Order
and Delivery
entity are mapped to the ORDERS
table.
This refactoring is primarily in this commit.
Let’s look at the changes.
New Gradle modules
Two new modules were added to settings.gradle
:
include "ftgo-delivery-service-api"
include "ftgo-delivery-service"
The purpose of each module is as follows:
-
ftgo-delivery-service-api
- contains theDeliveryService
interface, which is the entry point intoDelivery Management
from the monolith -
ftgo-delivery-service
- contains theDelivery Management
functionality
DeliveryService
interface
The DeliveryService
interface defines the coarse-grained, remotable API used by Order Management
to schedule and cancel deliveries.
public interface DeliveryService {
void scheduleDelivery(LocalDateTime readyBy, Long orderId);
void cancelDelivery(long orderId);
}
OrderService
now calls DeliveryService
The delivery management functionality has been moved out of OrderService
into DeliveryService
:
@Transactional
public class OrderService {
@Transactional
public Order cancel(Long orderId) {
...
deliveryService.cancelDelivery(order.getId());
...
}
public void accept(long orderId, LocalDateTime readyBy) {
...
deliveryService.scheduleDelivery(readyBy, order.getId());
...
}
Methods, such as accept()
and cancel()
invoke delivery management by calling the DeliveryService
, which is injected into the OrderService
.
DeliveryController
class
The DeliveryController
class implements the REST endpoints invoked by the courier’s mobile application.
It calls the DeliveryCourierService
, which is described below.
DeliveryServiceImpl
class
The DeliveryServiceImpl
class implements the delivery management logic that was previously in the DeliveryService
.
public class DeliveryServiceImpl implements DeliveryService, DeliveryCourierService {
public void scheduleDelivery(LocalDateTime readyBy, Long orderId) {...}
public void cancelDelivery(long orderId) {...}
...
It also implements the DeliveryCourierService
interface, which defines methods for updating the state of Courier
s and Delivery
s:
public interface DeliveryCourierService {
void notePickedUp(long orderId);
void noteDelivered(long orderId);
void updateAvailability(long courierId, boolean available);
}
This interface is separate (i.e. Interface segregation principle) from DeliveryService
since its called by DeliveryController
rather than the OrderService
.
Delivery Management
domain classes
The ftgo-delivery-service
module defines a self-contained set of domain classes including the following JPA @Entity
classes:
Delivery
- represents a deliveryDeliveryCourier
- represents a courierDeliveryRestaurant
- represents a restaurant
These classes mirror those in the monolith and are mapped to the same tables.
Each class contains just those fields that are needed by Delivery Management
.
For example, Delivery
is mapped to the ORDERS
table:
@Entity
@Table(name = "orders")
@Access(AccessType.FIELD)
@DynamicUpdate
public class Delivery {
private Long id;
private DeliveryState deliveryState;
private DeliveryRestaurant restaurant;
private DeliveryCourier assignedCourier;
private Address deliveryAddress;
private LocalDateTime deliveredTime;
private LocalDateTime pickedUpTime;
..
It contains only those fields from the original Order
class that are needed by Delivery Management
.
DeliveryState
is an enum, which is currently identical to the OrderState
.
A later step removes those values corresponding to states that are not relevant to Delivery Management
.
Similarly, DeliveryCourier
contains a subset of the fields from Courier
.
@Entity
@Access(AccessType.FIELD)
@DynamicUpdate
@Table(name = "courier")
public class DeliveryCourier {
private long id;
private Plan plan;
private Boolean available;
...
Delivery Management
does not create or delete these entities.
Instead, it only updates those fields of Delivery
and DeliveryCourier
that it owns.
In addition to those entity classes, the ftgo-delivery-service
module also contains the @Embeddable
Plan
Action
classes.
Delivery Management
creates, updates and deletes instances of these classes since it owns them.
Preserving read-only fields in ftgo-order-service
In order to minimize the scope of this refactoring step, the delivery-related fields (of classes) in ftgo-order-service
are preserved as readonly fields.
For example, the Courier
class still has an available
field even though the methods that update it have been removed from the class.
Similarly, ftgo-order-service
still contains read-only classes, such as Plan
and Action
.
These fields are updated by being mapped to the same database columns as the corresponding read-write fields in ftgo-delivery-service
.
Preserving these read-only fields eliminates the need to change the code that queries the delivery information.
For example, OrderController
obtains an Order
’s assigned Courier
and their pickup/dropoff schedule from the Order
:
public class OrderController {
private GetOrderResponse makeGetOrderResponse(Order order) {
return new GetOrderResponse(order.getId(),
...
order.getAssignedCourier() == null ? null : order.getAssignedCourier().getId(),
order.getAssignedCourier() == null ? null : order.getAssignedCourier().actionsForDelivery(order)
);
}
If these fields had been deleted, then the OrderController
would need to be changed to fetch the delivery information from the ftgo-delivery-service
.
What’s next
- Study the refactored code, which is the
extract-delivery-service-01-split-code
branch in the main repository. This refactoring is primarily in this commit. - Read the next step, which splits the database.
- Read chapter 13 of my book Microservices patterns, which covers refactoring to microservices
- Talk to me about my microservices consulting and training services