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 the DeliveryService interface, which is the entry point into Delivery Management from the monolith

  • ftgo-delivery-service - contains the Delivery 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 Couriers and Deliverys:

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 delivery
  • DeliveryCourier - represents a courier
  • DeliveryRestaurant - 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



Copyright © 2024 Chris Richardson • All rights reserved • Supported by Kong.

About Microservices.io

Microservices.io is brought to you by Chris Richardson. Experienced software architect, author of POJOs in Action, the creator of the original CloudFoundry.com, and the author of Microservices patterns.

ASK CHRIS

?

Got a question about microservices?

Fill in this form. If I can, I'll write a blog post that answers your question.

NEED HELP?

I help organizations improve agility and competitiveness through better software architecture.

Learn more about my consulting engagements, and training workshops.

LEARN about microservices

Chris offers numerous other resources for learning the microservice architecture.

Get the book: Microservices Patterns

Read Chris Richardson's book:

Example microservices applications

Want to see an example? Check out Chris Richardson's example applications. See code

Virtual bootcamp: Distributed data patterns in a microservice architecture

My virtual bootcamp, distributed data patterns in a microservice architecture, is now open for enrollment!

It covers the key distributed data management patterns including Saga, API Composition, and CQRS.

It consists of video lectures, code labs, and a weekly ask-me-anything video conference repeated in multiple timezones.

The regular price is $395/person but use coupon NPXJKULI to sign up for $95 (valid until December 25th, 2024). There are deeper discounts for buying multiple seats.

Learn more

Learn how to create a service template and microservice chassis

Take a look at my Manning LiveProject that teaches you how to develop a service template and microservice chassis.

Signup for the newsletter


BUILD microservices

Ready to start using the microservice architecture?

Consulting services

Engage Chris to create a microservices adoption roadmap and help you define your microservice architecture,


The Eventuate platform

Use the Eventuate.io platform to tackle distributed data management challenges in your microservices architecture.

Eventuate is Chris's latest startup. It makes it easy to use the Saga pattern to manage transactions and the CQRS pattern to implement queries.


Join the microservices google group

Topics

Note: tagging is work-in-process

Cynefin   ·  DDD   ·  GitOps   ·  Microservices adoption   ·  ancient lore   ·  anti-patterns   ·  api gateway   ·  application api   ·  application architecture   ·  architecting   ·  architecture   ·  architecture documentation   ·  assemblage   ·  automation   ·  beer   ·  books   ·  build vs buy   ·  containers   ·  culture   ·  dark energy and dark matter   ·  decision making   ·  deployment   ·  deployment pipeline   ·  design-time coupling   ·  developer experience   ·  development   ·  devops   ·  docker   ·  eventuate platform   ·  fast flow   ·  generative AI   ·  glossary   ·  health   ·  hexagonal architecture   ·  implementing commands   ·  implementing queries   ·  inter-service communication   ·  kubernetes   ·  loose coupling   ·  microservice architecture   ·  microservice chassis   ·  microservices adoption   ·  microservices rules   ·  microservicesio updates   ·  modular monolith   ·  multi-architecture docker images   ·  observability   ·  pattern   ·  pattern language   ·  refactoring   ·  refactoring to microservices   ·  resilience   ·  sagas   ·  security   ·  service api   ·  service architecture   ·  service blueprint   ·  service collaboration   ·  service design   ·  service discovery   ·  service granularity   ·  service template   ·  software delivery metrics   ·  success triangle   ·  survey   ·  tacos   ·  team topologies   ·  technical debt   ·  testing   ·  transaction management   ·  transactional messaging   ·  wardley mapping

All content


Posts

20 Mar 2024 » A tour of two sagas
21 Dec 2023 » Thoughts about team size
24 Jul 2017 » Revised data patterns