使用单一代码仓库管理微服务应用

如何组织源代码,是实现微服务架构中的一个现实问题。由于每个微服务相对独立,一种最直接的做法是为每个微服务使用各自独立的代码仓库。这种做法的好处很明显,就是从源头开始对微服务进行隔离。每个微服务有各自独立的持续集成和部署的流程。

每个微服务使用独立代码仓库的做法,会带来比较大的管理开销,尤其是微服务之间存在共享代码的情况。共享代码的存在,在很多情况下是无法避免的。比如,很多微服务都需要使用的公共库。当共享代码发生改变时,所有使用这些公共代码的微服务,都可能需要进行相应的修改。如何确保对公共代码的修改,能够在多个微服务中保持同步,是一个很现实又复杂的问题。

对于中小规模的微服务应用来说,另外一种比较现实的做法是使用单一的代码仓库。使用单一的代码库可以方便的管理共享代码。当需要对共享代码进行修改时,对共享代码自身的改动,以及对受影响的微服务的修改,都可以在同一个代码提交(commit)中完成。这非常有利于追踪代码的变化。使用单一代码库的弊端也有一些。由于只有一个代码库,当修改某个微服务的代码时,也会同时构建和部署其他不相关的微服务。

下面以我的《Quarkus云原生微服务开发实战》一书的实战应用来进行说明。该实战实用是一个 Maven 项目。最简单的组织代码的方式是把共享代码和微服务组织成 Maven 中的多个模块。如下图所示,所有模块以扁平化的方式来组织。

扁平化的组织结构

在主模块的 pom.xml 文件中,子模块的声明如下所示。

<modules>
  <module>restaurant-service-api</module>
  <module>restaurant-service</module>
  <module>common</module>
  <module>file-upload-service</module>
  <module>restaurant-data-loader</module>
  <module>restaurant-search-service</module>
  <module>restaurant-search-service-api</module>
  <module>order-service-api</module>
  <module>order-service</module>
  <module>common-db-entity</module>
  <module>common-event-api</module>
  <module>common-event-producer</module>
  <module>common-event-consumer</module>
  <module>delivery-service</module>
  <module>delivery-service-api</module>
  <module>rider-service-api</module>
  <module>rider-service</module>
  <module>common-json</module>
  <module>common-test</module>
  <module>mobile-api-graphql</module>
</modules>

这种扁平化的方式虽然简单,但是不容易看出来每个模块的职责。可以进一步优化为分层结构。可以把微服务应用的模块进行分类,并指定不同的子目录,如下表所示。

分类子目录
公共库lib
微服务 APIapi
微服务的实现service
与开发和管理相关的工具tool

通过这个分类,可以把应用的模块组织到对应的目录中。下图是修改之后的目录结构。从中可以看到,修改之后的目录结构层次清晰,更加方便组织和管理。

分类的组织结构

pom.xml中的模块声明如下所示。

<modules>
  <module>lib/common/base</module>
  <module>lib/common/json</module>
  <module>lib/common/test</module>
  <module>lib/common/grpc</module>
  <module>lib/db/entity</module>
  <module>lib/event/api</module>
  <module>lib/event/producer</module>
  <module>lib/event/consumer</module>
  <module>api/restaurant-service-api</module>
  <module>api/order-service-api</module>
  <module>api/restaurant-search-service-api</module>
  <module>api/delivery-service-api</module>
  <module>api/rider-service-api</module>
  <module>service/restaurant-service</module>
  <module>service/file-upload-service</module>
  <module>service/restaurant-search-service</module>
  <module>service/order-service</module>
  <module>service/delivery-service</module>
  <module>service/rider-service</module>
  <module>service/mobile-api-graphql</module>
  <module>tool/restaurant-data-loader</module>
</modules>

每个 Maven 模块的名称都遵循一定的命名规范,如下图所示。

Maven 模块

对于一个微服务架构的应用来说,推荐从单一代码仓库开始。在很多时候,这样简单的组织方式就足够了。

版权所有 © 2024 灵动代码