微服务之间如何共享DTO?

2,633 阅读3分钟

1. 概述

近些年来,微服务变得越来越流行。微服务基本特征是模块化、独立、易于扩展的。**它们之间需要协同工作并交换数据。**为了实现这一点,我们创建了名为 DTO 的共享数据传输对象。在本文中,我们将介绍在微服务之间共享DTO的方法。

2. 将域对象发布为DTO

使用微服务管理表示应用程序域的模型。域模型的关注点与 DTO 不同,我们将它们与DAO层中的数据模型分开。 这样做的主要原因是我们不想通过服务向客户暴露我们领域的复杂性。

恰恰相反,我们通过 REST API 暴露 DTO 为客户端提供服务。当DTO在这些服务之间传递时,我们将它们转换为域对象。

dto

上面的 面向服务架构 示意性地显示了DTO到域对象的组件和流程。

3. 微服务间共享DTO

以客户订购产品的过程为例。此过程基于 Customer-Order 模型,从服务体系结构的角度来看看这个过程。 假设客户服务将请求数据发送到订单服务:

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

CustomerOrder 服务使用 contracts (契约) 进行通信。contract(或者是服务请求)以JSON格式显示。作为 Java 模型,OrderDTO 类表示客户服务和订单服务之间的契约:

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-client模块具有以下结构:

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

OrderClient 是一个接口,它定义了处理订单请求的order方法:

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

为了实现 order 方法,我们使用 RestTemplate 对象向 order 服务发送POST请求:

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

此外,order-client模块已经可以使用了。它现在成为 customer-service 模块的依赖库:

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

当然,如果 order-server 模块没有向 order-client 暴露 /create 服务端点,那也是不行滴!

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

由于这个服务端点,Customer Service 可以通过其order客户端发送订单请求。通过使用客户端模块,微服务以更加独立的方式相互通信。DTO中的属性在客户端模块中更新。因此,违背契约仅限于使用相同客户端模块的服务。

4. 结论

本文解释了一种在微服务之间共享DTO对象的方法。充其量,我们通过签订特殊契约作为微服务客户端模块(库)的一部分来实现这一点。通过这种方式,我们将服务客户端与包含API资源的服务端部分分开。这样做的好处是:

  • 服务之间没有冗余
  • 违反契约仅限于使用同一客户端的服务