Управление общей кодовой базой в микросервисной архитектуре

При большом количестве микросервисов в проекте приходится сталкиваться с тем, что в некоторых из них нужно дублировать один и тот же код, а обнаружив баг в одном месте, искать и исправлять его везде. При этом, если микросервисы поддерживаются разными разработчиками, то каждый будет исправлять баг по-своему и в дальнейшем будет сложнее привести всё к единообразию. Давайте на примере рассмотрим несколько подходов к переиспользованию кода в микросервисах.

Представим, что у нас есть сайт ветеринарной клиники, где можно зарегистрироваться, выбрать доктора и записаться к нему на прием. Проект может содержать множество микросервисов, но мы выделим для рассмотрения следующие:

  • user-service — отвечает за авторизацию, регистрацию и за работу с пользователями (как врачами, так и владельцами питомцев).

  • notification-service — отвечает за email рассылку (это могут быть новости, напоминания о приеме и тд.)

  • gateway — микросервис, который будет агрегировать ответы от внутренних микросервисов (таких, как user-service) и подготавливать ответ для фронтенда.

Категории микросервисов

В нашем примере микросервисы можно разделить на несколько категорий:

  • микросервисы, которые используют базу данных и не предоставляют никакие API методы. В списке выше это notification-service, который делает рассылки по расписанию или использует какой-нибудь брокер сообщений (типа kafka или rabbitMQ).

  • микросервисы, которые не используют базу данных, а общаются только со сторонними API и предоставляют свои API методы для взаимодействия. В нашем примере это gateway, который только обрабатывает ответы от других микросервисов.

  • микросервисы, которые используют и базу данных и предоставляют свои API методы для работы (как, например, user-service).

Что, если мы хотим, чтобы все эти микросервисы следовали каким-то внутренним стандартам? Например, те, которые предоставляют API методы для взаимодействия, должны содержать автодокументацию с описанием всех методов посредством OpenAPI, а логирование во всех микросервисах должно быть настроено так, чтобы все логи отправлялись в splunk. В этом случае нам придется дублировать конфигурации и какие-то утилитные классы для работы с датами, ценами и прочим в каждом микросервисе. Для того, чтобы этого избежать, нам подойдёт своя библиотека, в которой мы будем хранить весь переиспользуемый код. Рассмотрим несколько возможных реализаций данной библиотеки.

Одна библиотека для всех микросервисов

Мы можем создать новый maven проект, в котором мы подключим все зависимости, которые могут быть использованы в микросервисах. В этом случае в каждом микросервисе будет большое количество неиспользуемых зависимостей, что повлечет за собой увеличение размера финального jar файла. Помимо этого придется строго следить за тем, чтобы не создавались новые бины без надобности (зачем нам бин для работы с базой данных, если он не нужна в микросервисе?).

Множество разных общих библиотек

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

Одна общая многомодульная библиотека

В этом случае все изменения в одном пулл реквесте, нет никаких лишних зависимостей и бинов в микросервисах, публиковать можно как отдельные версии подпроектов так и выпускать одну версию. Давайте подробнее рассмотрим, как же реализовать подобную библиотеку с использованием maven.

Для примера, определимся с подмодулями, которые нам могут потребоваться, и что в них будет находиться:

  • core — в нем мы будем хранить что-то базовое для всех микросервисов. Например, какие-то наши базовые исключения типа PlatformException или ApplicationException. Также там можно хранить какие-то базовые структуры данных, типа Pair, Timer и все подобное, что может использоваться в любом нашем микросервисе вне зависимости от его назначения.

  • utils — в этом модуле будем хранить утилитные классы общего назначения. Например, классы для работы с датами, ценами, json и тп.

  • database подмодуль будет содержать какие-то полезные вещи для работы с базой данных. Это могут быть кастомные сериалайзеры / десериалайзеры полей базы данных и многое другое. В нашем примере этот подмодуль могут использовать user-service, notification-service.

  • logging — в нем мы сможем описать конфигурацию для направления всех наших логов в splunk.

  • client — в этом подмодуле мы сможем хранить всё, что может потребоваться в микросервисах, которые взаимодействуют с другими API. Его может использовать gateway.

  • api — здесь мы будем хранить все для сервисов, которые будут предоставлять методы API. Например, конфигурацию для автогенерации документации и тому подобное. Его можно подключить в user-service и gateway.

  • любые другие подмодули, который вам могут понадобиться.

Описание многомодульного проекта

Давайте начнем с описания базового pom файла. В качестве родителя будем использовать pom файл из предыдущей статьи для того, чтобы подключаемые в подмодулях зависимости имели те же версии что и во всех микросервисах.



	4.0.0
	
		com.example
		base-pom
		0.0.1-SNAPSHOT
	
	com.example.common
	common-library-parent
	0.0.1-SNAPSHOT
	pom

	common-library-example-parent
	Demo project

	
		core
        utils
		database
        logging
        client
        api
	

В каждый подмодуль нужно добавить свой pom файл. В нем можно добавить все зависимости, необходимые в конкретном подмодуле, включая зависимости на другие подмодули. Для примера возьмем pom файл из подмодуля client, который требует зависимость на feign (подробнее о том, как его использовать я писал в своей статье) и исключения из подмодуля core. Таким образом pom файл будет выглядеть следующим образом:



	4.0.0
	
		com.example
		common-library-parent
		0.0.1-SNAPSHOT
	
	client
	${project.parent.version}
	client
	Demo project

	
		
			${project.groupId}
			core
			${project.version}
		
        
        	io.github.openfeign
        	feign-spring4
        
	

В подмодуле мы можем добавить классы для расширения возможностей feign клиента. Например, свою реализация логирования запросов / ответов от API и многое другое.

Финальная структура проекта будет выглядеть следующим образом:

Структура проекта

Структура проекта

Подключение многомодульной библиотеки в микросервисы

Для того, чтобы использовать данную библиотеку, как и в случае с parent pom, ее сначала нужно опубликовать в локальный репозиторий (или же в другом хранилище, например, nexus) командой mvn install Вы увидите следующее:

987a8b2c3f2da4cba13158fd6c1aae83.png

Далее вы уже сможете подключить необходимые подмодули в микросервисы. Подключим подмодуль client в наш микросервис gateway для того, чтобы легко добавить в нем интеграцию с user-service:


	0.0.1-SNAPSHOT



  
		com.example
		client
		${common-lib.version}
	

Заключение

Конечно, количество подмодулей может быть любым. Все зависит от того, что именно требуется в вашем проекте.

Полезные ссылки

© Habrahabr.ru