Перейти к основному содержимому

Как разделить DTO между микросервисами

· 4 мин. чтения

1. Обзор

Микросервисы стали популярны в последние годы. Одной из важнейших характеристик микросервисов является их модульность, изоляция и простота масштабирования. Микросервисы должны работать вместе и обмениваться данными. Для этого мы создаем общие объекты передачи данных, называемые DTO.

В этой статье мы представим способы совместного использования DTO между микросервисами.

2. Предоставление объектов домена как DTO

Модели, представляющие домен приложения, управляются с помощью микросервисов. Модели предметной области — это разные задачи, и мы отделяем их от моделей данных на уровне DAO.

Основная причина этого заключается в том, что мы не хотим раскрывать клиентам сложность нашего домена через службы. Вместо этого мы предоставляем DTO между нашими службами, которые обслуживают клиентов приложений через REST API. Пока DTO проходят между этими сервисами, мы конвертируем их в объекты предметной области .

./749547ed0d658ebc7d93493a2cde1099.png

Приведенная выше сервис-ориентированная архитектура схематически показывает компоненты и поток DTO для объектов домена.

3. Совместное использование DTO между микросервисами

Возьмем, к примеру, процесс заказа товара покупателем. Этот процесс основан на модели « клиент-заказ ». Давайте посмотрим на процесс со стороны сервисной архитектуры.

Предположим, что служба Customer отправляет данные запроса в службу Order следующим образом:

"order": {
"customerId": 1,
"itemId": "A152"
}

Сервисы Customer и Order взаимодействуют друг с другом с помощью контрактов . Контракт, который в противном случае является запросом на обслуживание, отображается в формате JSON. В качестве модели Java класс OrderDTO представляет контракт между службой Customer и службой Order:

public class OrderDTO {
private int customerId;
private String itemId;

// constructor, getters, setters
}

3.1. Совместное использование DTO с помощью клиентских модулей (библиотек)

Микрослужбе требуется определенная информация от других служб для обработки любого запроса. Допустим, есть третий микросервис, который получает запросы на оплату заказа. В отличие от сервиса «Заказ», этот сервис требует другой информации о клиенте:

public class CustomerDTO {
private String firstName;
private String lastName;
private String cardNumber;

// constructor, getters, setters
}

Если мы также добавим службу доставки, информация о клиенте будет иметь:

public class CustomerDTO {
private String firstName;
private String lastName;
private String homeAddress;
private String contactNumber;

// constructor, getters, setters
}

Таким образом, размещение класса CustomerDTO в общем модуле больше не служит намеченной цели. Чтобы решить эту проблему, мы подходим к другому методу.

Внутри каждого микросервисного модуля создадим клиентский модуль (библиотеку) и рядом с ним серверный модуль :

order-service
|__ order-client
|__ order-server

Модуль order-client содержит DTO, совместно используемый службой поддержки клиентов. Таким образом, модуль заказа-клиента имеет следующую структуру:

order-service
└──order-client
OrderClient.java
OrderClientImpl.java
OrderDTO.java

OrderClient — это интерфейс, который определяет метод заказа для обработки запросов заказа:

public interface OrderClient {
OrderResponse order(OrderDTO orderDTO);
}

Для реализации метода заказа мы используем объект RestTemplate для отправки POST-запроса в сервис Order:

String serviceUrl = "http://localhost:8002/order-service";
OrderResponse orderResponse = restTemplate.postForObject(serviceUrl + "/create",
request, OrderResponse.class);

Кроме того, модуль заказ-клиент готов к использованию. Теперь он становится зависимой библиотекой модуля обслуживания клиентов :

[INFO] --- maven-dependency-plugin:3.1.2:list (default-cli) @ customer-service ---
[INFO] The following files have been resolved:
[INFO] com.foreach.orderservice:order-client:jar:1.0-SNAPSHOT:compile

Конечно, это не имеет смысла без модуля сервера заказов, который предоставляет конечную точку службы «/create» клиенту заказа:

@PostMapping("/create")
public OrderResponse createOrder(@RequestBody OrderDTO request)

Благодаря этой конечной точке службы служба клиентов может отправлять запрос заказа через свой клиент заказа. С помощью клиентского модуля микрослужбы взаимодействуют друг с другом более изолированным образом. Атрибуты в DTO обновляются в клиентском модуле. Таким образом, нарушение контракта ограничивается службами, использующими один и тот же клиентский модуль.

4. Вывод

В этой статье мы объяснили способ совместного использования объектов DTO между микросервисами. В лучшем случае мы достигаем этого, заключая специальные контракты в составе клиентских модулей (библиотек) микросервисов. Таким образом мы отделяем клиент сервиса от серверной части, содержащей ресурс API. В результате есть некоторые преимущества :

  • В коде DTO между сервисами нет избыточности.
  • Нарушение контракта ограничено службами, использующими одну и ту же клиентскую библиотеку.

Пример кода приложения Spring Boot доступен на GitHub .