Microservices Shared Libraries — Design and Best Practices (2024)

Microservices Shared Libraries — Design and Best Practices (3)

Microservices is a buzzword that any software engineer has heard about.

We love microservices, right? This architecture helps you divide the application into small self-contained applications, with significant advantages, such as scaling faster and at lower cost, a smaller and readable codebase, developing and releasing features faster if planned correctly.

HOWEVER, it also introduces several difficulties, like

  • communication — requires network communication between different microservices,
  • debugging — the code and logs are distributed,
  • more complex architecture — usually microservices are used with a domain-driven design that requires more effort to design correctly.

Luckily there are many solutions and tools to help us to overcome these and other challenges we face when developing microservices.

Another challenge microservice architecture creates is code duplication, which will be the focus of this post, as well as how shared libraries help us reduce our code duplication.

Why shared libraries are important

Shared libraries are the key solution for code duplication between microservices.

One of the most common examples of the need for shared libraries is logging.
Logging can have custom logic, like formatting or hiding sensitive information, such as customers’ addresses and phone numbers.

Now imagine each microservice having its own implementation, how many developer hours will be wasted creating the same implementation? What if it’s not the exact same implementation across the different microservices?
Aggregating logs will become an even harder task, two similar log records may be labeled differently because of small changes in the implementation.

Another point worth mentioning is the recently discovered log4j vulnerability, which requires much more effort in microservice architecture, as you need to identify and fix per microservice.
Alternatively, if you have your own logging library that uses log4j internally, you only need to protect yourself from this vulnerability at a single point.

Logging appears in any microservice you create (hopefully), and it isn’t dependent on any of them, so it’s a great example for a shared library.
Other great examples for shared libraries are security, monitoring, async communication, and exceptions.

Why it is important to get it right

Microservices architecture has been created to decouple the different parts of the application (amongst other reasons). Shared libraries do the opposite — it’s a common code shared by all the microservices.
It means that if you don’t do it well, it has the potential to cancel out one of the biggest benefits granted by the microservices architecture! The result is a weird mix of parts, like Frankenstein’s monster.

Don׳t create a single library

There are several ways you can manage your shared libraries, it’s considered best to either create a different repository for each needed library or a single repository (A.K.A monorepo) with multiple libraries. The important thing is that in one way or another they are separated.
For example, introduce a single repository that includes a project for monitoring, security, logging — each of them will be self-contained (except if there are needed dependencies).

At Duda, we prefer to have a single repository that includes different projects, there are multiple reasons we like this approach, such as:

  • Usually, each library (in our case anyway) is quite slim
  • We can have a single Jenkins pipeline for the build and release of all the libraries
  • It makes it easier to have dependencies (when needed) between libraries under the same repository
  • When building a new release we create the same version for all the libraries, so when consuming the libraries we can have a single version for all of them

Each library can be structured any way you want, but there are 2 possible structures that fit particularly well:

  1. A simple src folder containing all your code
  2. Dividing the library into 3 separate projects: api, impl (for implementation), and test-kit — with the impl and test-kit being dependent on the api project

Let’s dive deeper into the second structure…

  • The api project contains all the interfaces and classes that are used by the library’s client
  • The impl project has all the actual logic
  • The test-kit project contains mocking and testing support — for example, if your library uses rest calls you will probably want to mock the request and response

The separation into 3 different projects allows you to encapsulate the library implementation details, with 3 major benefits:

  1. Avoids breaking the contract with the users when changing the implementation details
  2. You can use the impl project, and while testing be dependent on the test-kit. This introduces the library’s client to a much better experience while testing
  3. Build tool optimization — using the test-kit project doesn’t require loading the impl project. Even more, some build tools have cache mechanisms (such as gradle), so it doesn’t need to rebuild projects that have not been changed

Let’s take a closer look at a real-life example from Duda.
To have a unified behavior of feature flags across our microservices, we have a shared library that uses LaunchDarkly internally.

LaunchDarkly is a platform to manage feature flags, using it you can manage features in production, control which users have access to them and when.
If you have a problem with a new feature? No problem, you can turn off the relevant feature flag and it will be disabled in production until further investigation, no need to redeploy.

While testing our microservices, we don’t want actual network calls to LaunchDarkly and to get the real values, but defining the values of the relevant flags for each test case.
Instead each microservice will need to mock or create a test implementation, we already have it set up and ready in the test-kit project, all we need to do is to import it and we are ready to go.

To help you understand it better, here is a tiny snapshot from our code:

FeatureFlags — interface in flags-api

LaunchDarklyFeatureFlags — actual implementation in flags-impl (not in above snapshot), implements FeatureFlags methods, such as getBoolean(String flag)

InMemoryFeatureFlags — implementation in flags-test-kit that implements its methods. But instead of getting the actual flag from LaunchDarkly (or other platforms, the api and test-kit projects are not platform locked!) we get it from memory, and can set it using setBoolean method

My advice to you is to take your time while designing the api and test-kit, as it can save you a lot of time later!

The last thing you want to do is create a poorly designed library that will introduce potential backward compatibility breaks in the future.

Encapsulation

It is important to encapsulate the internal decisions and logic from the user, it’s essential to programming in general, and specifically to a shared library.

Let’s say you create a library with vendor-specific code, for example, a library to upload images to storage, it is critical you create the interface/classes that are used by the client generically.
You should avoid names such as “S3ImageUploader” (s3 is an Amazon service for file storage).
This is because, if later on you want to move to Azure Blob (Microsoft equivalent service for amazon S3), all your clients will need to fix the method signature.

It’s better to use names such as “ImageUploader” for the interfaces that are exposed to the user.

Mitigate the damage

Sometimes you must break the code, for example, you must replace a library used internally because of a newfound vulnerability that affects the output.

  1. Use semantic versioning, so your versions follow the pattern MAJOR.MINOR.PATCH, allowing your clients to upgrade the versions appropriately. Change in the major version let’s people know that this version might introduce a compatibility break, so they can do their research and decide whether to upgrade or not.
    Semantic versioning might be messy to manage manually, don’t worry Conventional commits to the rescue! (more about it later on)
  2. Another option is instead of modifying the existing interface behavior, introducing a new interface with the new logic inside — you can use parts or all of the existing code as well, mark the old interface as deprecated and don׳t support any new functionality to the old interface, but only the new one. This will help push your clients to adopt the new interface (with the new behavior)

Remember — many people might work on the shared library, don’t make it into a monolith!
Give a lot of thought before introducing a new library, and when you create one try to think how it might change and who might use it. Don’t be tempted to create it for your own specific needs, making it hard for others to use or extend.

Don’t write any domain-specific code inside! Even shared code that is business-related is probably not supposed to be there!
For example, a User model that begins the same for all the microservices is still domain-related logic, and it probably doesn’t need to be in the library, even if it means that each microservice that uses this model needs to duplicate it. The reason is that different microservices might need to change it in the future to fit their business needs. It makes no sense that they all work with the same model, it can introduce fields or logic that aren’t related to other MS or might even break them, if they want to rename or change some of the logic.

When using a single repository, we find Conventional commits to be very helpful to communicate the changes we introduce to the code.

TL;DR

Use conventions for the commits, such as starting each commit with a fix — for a bug fix (related to patch), feat — for introducing a new feature (related to minor in versioning), and BREAKING CHANGE — for (you guessed it) breaking change in the API (related to major in versioning).
This helps a lot when someone else tries to understand the history of the repository, what was done and where in the code.

There are many great tools to help with automation here, some of them are action-semantic-pull-request to enforce conventional commits and standard version to bump the version and create a changelog according to the conventional commits.

After reading this article, you’re encouraged to try to create shared libraries that will help your organization avoid code duplication and a lot of wasted time fixing the problems introduced by poorly written shared libraries.

Best practices today might not be the best practices of tomorrow, so you are welcome to try new approaches, but only after you understand why and how we design and implement shared libraries the way we do today.

Let’s Duda this!

Microservices Shared Libraries — Design and Best Practices (2024)

FAQs

What are shared libraries in microservices? ›

In a nutshell, if two microservices are expected to share code, the code can be placed in a shared library. This essentially means that the code is extracted from the microservice and packaged so that other microservices can use it.

What is the best way to share code between microservices? ›

The best way is via NuGet packages without versioning issues, the second is copy-pasta. Microservices are stand alone they really should not have much in common.

What are the best practices for Spring Boot microservices? ›

Best practices for building Spring Boot microservices include designing the services around the business domain, creating small, well-defined services, implementing API gateway to handle requests from outside, monitoring service metrics, and using centralized configuration.

What is the difference between microservices and REST services? ›

REST APIs can be used within monolithic applications as well, but Microservices architecture specifically emphasizes the decomposition of applications into smaller services.

What are the most used microservices design patterns? ›

Before incorporating any best practice the first step is to understand the microservices design practices you might frequently use during development.
  • Circuit breaker design pattern. ...
  • Command query responsibility segregation (CQRS) ...
  • Asynchronous messaging. ...
  • Event sourcing. ...
  • Strangler. ...
  • Decomposition patterns.
Jan 7, 2024

How do I call one microservice to another microservice? ›

In order to make a call, a microservice will need to know the endpoint of the called REST API and have the authentication to access this data or information. After the calling service sends the request, the called service will process it and provide a response.

Can multiple microservices share the same server? ›

Sharing a common database between multiple Microservices increases coupling between them. One service can start accessing data tables of another service. This can defeat the purpose of bounded context. So it is not a good idea to share a common database between Microservices.

How do you communicate between two services in microservices? ›

Microservices can communicate asynchronously through messaging queues like RabbitMQ, Apache Kafka, or Amazon SQS. With messaging queues, services can publish messages to a queue, and other services can consume these messages. This decouples communication between services and supports event-driven architectures.

What is the best way to deploy microservices? ›

The top 5 ways to deploy microservices are:
  1. Individual host for single instance. In this method each instance has separate hosts or VM for them. ...
  2. Multiple instances on one host or VM. ...
  3. Containerization. ...
  4. Container Orchestration. ...
  5. Serverless deployment. ...
  6. Rolling Deployment. ...
  7. Blue Green. ...
  8. Canary.
Nov 1, 2023

What is the best practice of microservices caching? ›

Here are some key considerations to keep in mind when implementing caching in microservices:
  • Data Volatility and Freshness: Evaluate the volatility of your data. ...
  • Data Granularity: Identify the appropriate level of granularity for caching. ...
  • Cache Invalidation: Plan how to invalidate cached data when it becomes outdated.
Sep 12, 2023

Which API gateway is best for microservices Spring Boot? ›

Spring Cloud Gateway is a framework for building microservices-based applications.

What is one of the key design principles of microservices? ›

Single Responsibility Principle

Each service must handle a distinct functionality or business domain in microservices. By adhering to the Single Responsibility Principle, developers design modules with a single, well-defined purpose, making them easier to understand, maintain, and scale.

What is the best technology to create microservices? ›

Best Technologies for Microservices
  1. Java. Annotation syntax, which is easy to read, is the key factor that makes Java a great programming language for developing microservices. ...
  2. Golang. If you want to enhance your existing project, the Golang can be a good choice for microservices development. ...
  3. Python. ...
  4. Node JS. ...
  5. 5. .

Top Articles
Jaw-Dropping Stats About the State of Debt in America
Towards sustainable governance: The role of the director in ESG issues
My Arkansas Copa
Hotels Near 625 Smith Avenue Nashville Tn 37203
Unit 30 Quiz: Idioms And Pronunciation
Avonlea Havanese
Blanchard St Denis Funeral Home Obituaries
Tj Nails Victoria Tx
How To Get Free Credits On Smartjailmail
Words From Cactusi
Barstool Sports Gif
Overzicht reviews voor 2Cheap.nl
Bros Movie Wiki
Voyeuragency
Uhcs Patient Wallet
Funny Marco Birth Chart
iLuv Aud Click: Tragbarer Wi-Fi-Lautsprecher für Amazons Alexa - Portable Echo Alternative
Mzinchaleft
NHS England » Winter and H2 priorities
Unity - Manual: Scene view navigation
Swgoh Blind Characters
Raz-Plus Literacy Essentials for PreK-6
Governor Brown Signs Legislation Supporting California Legislative Women's Caucus Priorities
Hampton University Ministers Conference Registration
Understanding Gestalt Principles: Definition and Examples
How to Make Ghee - How We Flourish
Idle Skilling Ascension
Kirk Franklin Mother Debra Jones Age
Kroger Feed Login
Craftsman Yt3000 Oil Capacity
Nail Salon Open On Monday Near Me
Bt33Nhn
CVS Near Me | Somersworth, NH
Tds Wifi Outage
Laff Tv Passport
Mckinley rugzak - Mode accessoires kopen? Ruime keuze
At Home Hourly Pay
13 Fun & Best Things to Do in Hurricane, Utah
Pain Out Maxx Kratom
Thotsbook Com
Here's Everything You Need to Know About Baby Ariel
Lady Nagant Funko Pop
Reilly Auto Parts Store Hours
Crigslist Tucson
Terrell Buckley Net Worth
Food and Water Safety During Power Outages and Floods
Diccionario De Los Sueños Misabueso
Sam's Club Fountain Valley Gas Prices
Tenichtop
Acellus Grading Scale
Latest Posts
Article information

Author: Terence Hammes MD

Last Updated:

Views: 5725

Rating: 4.9 / 5 (49 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Terence Hammes MD

Birthday: 1992-04-11

Address: Suite 408 9446 Mercy Mews, West Roxie, CT 04904

Phone: +50312511349175

Job: Product Consulting Liaison

Hobby: Jogging, Motor sports, Nordic skating, Jigsaw puzzles, Bird watching, Nordic skating, Sculpting

Introduction: My name is Terence Hammes MD, I am a inexpensive, energetic, jolly, faithful, cheerful, proud, rich person who loves writing and wants to share my knowledge and understanding with you.