attheoaks.com

# Defining Microservices: A Comprehensive Guide to Best Practices

Written on

Understanding Microservices Architecture

When your team embarks on the journey of implementing microservices, several pivotal questions often arise: How should you delineate your microservices? What boundaries should you establish for them? How many microservices are appropriate for your application or organization?

In the past, our team managed a robust monolithic application that served its purpose admirably for over ten years. However, as it became a hindrance to our advancement, we made the decision to decompose the application into smaller modules. Transitioning to microservices was a logical step as we modernized both our application and its underlying platform. Yet, we needed to carefully consider the number of microservices we intended to develop.

Creating too few microservices can lead to applications that still function like monolithic systems, while an excess can complicate maintenance. Below are key considerations our team evaluated during our transition to a microservices architecture, which may prove beneficial for refining your own design.

High Cohesion with Loose Coupling

Services should maintain high cohesion by focusing on a single functional purpose. Each service should only address one specific business need and avoid mixing multiple functionalities. For instance, while a customer is necessary to create an order, the service responsible for order creation should not also handle customer creation. Instead, the latter should be an independent microservice, allowing the order creation service to receive the customer ID as input after the customer is established.

Ensuring high cohesion while keeping services loosely coupled is crucial. Services should avoid direct dependencies on external code or databases whenever possible. In our order creation example, the order service shouldn't directly invoke the customer creation service; rather, it should handle customer IDs independently. This design prevents the order service from failing if the customer service is temporarily unavailable, allowing it to function with existing customer IDs without being affected by changes in the customer service.

Minimizing Database Interaction

To keep services narrowly focused, they should interact with as few database tables as necessary. The primary consideration is to engage with tables that contain transactional data, while not factoring in static lookup tables. Ideally, each service should rely on just one functional table interaction, although existing database structures from a monolithic application may complicate this goal. It’s essential not to force a one-table constraint if it doesn't align with your current database design.

Adapting to Varying Change Speeds

If functionalities exhibit different rates of change, they should be developed as independent services. For example, if functionality A frequently undergoes updates while functionality B remains stable, it’s wise to deploy them as separate microservices. This separation allows each service to maintain its own build, deployment, and operational cycles, reducing the risk of one service inadvertently affecting the other.

Independent Lifecycle Management

Features that can function with their own independent lifecycle—encompassing coding, building, testing, and deployment—should be considered as standalone microservices. Even if two features change at similar frequencies, bundling them into a single microservice can hinder independent development and deployment. This independence empowers teams to introduce new features and enhancements without waiting for others to finish their tasks.

Scaling Requirements

Different functionalities may have distinct scaling requirements. Some services may need to scale rapidly due to high demand at certain times, while others maintain a steady load throughout the year. For instance, a product viewing service may experience spikes during holiday sales, while warranty inquiries might peak after purchases. These differing demands suggest that such features should be developed and scaled as independent microservices.

Transaction Speed Variation

When functionalities demand different transaction speeds, they should be implemented as separate microservices, ideally with distinct data stores. A service with slow performance can monopolize database resources, degrading the performance of faster services if they share the same pool. By isolating slower services with their own build and deployment processes, you can prevent performance bottlenecks.

Business Group Ownership

If your application serves multiple business units, it’s critical to segregate functionalities based on ownership. This division helps avoid conflicting requirements and potential defects that may arise from shared codebases. By creating distinct services for different business groups, you mitigate the risk of unintended impacts during code changes.

Additional Considerations

Keep the following points in mind when determining microservice boundaries:

  • Avoid creating overly small (nano) services, as they can generate unnecessary complexity. If two functions consistently call each other, consider consolidating them into a single service.
  • Ensure that no duplicate information is disseminated across services. Each piece of data should have a single source, with other services utilizing identifiers to access necessary details.
  • Leverage the experience of your team. Skilled developers can more accurately define microservice boundaries, so having at least one expert can significantly aid in this process.
  • Reassess your database structure. A single database for all microservices can become a bottleneck, so adopting a micro DB architecture may be beneficial for optimizing service independence.

Final Thoughts

The considerations outlined here focus on accurately defining microservice boundaries. While there is no one-size-fits-all approach, it's essential to create services that meet the outlined criteria. Don't feel pressured to achieve the perfect number or size of services from the outset; requirements will evolve over time, and your understanding of both functional and non-functional needs will deepen.

Instead of striving for perfection at the beginning, aim to launch a functional system in production. Learning from real-world performance will enable you to refine your service architecture over time.

I welcome your insights and experiences with microservices. Please share your thoughts in the comments section!

Thank you for reading this article. You may also find interest in the following resources:

This video offers insights on designing microservice architectures effectively.

In this video, microservices architecture is explained in a way that is accessible to everyone, making it a valuable resource for understanding the concepts discussed.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Transform Emojis into Custom Image Files with JavaScript Magic

Discover how to convert emojis into images with JavaScript, enhancing visual communication for various projects.

Top 10 Essential DevOps Tools for 2023: A Comprehensive Guide

Discover the top 10 DevOps tools for 2023 that enhance software development and delivery, and learn how they can revolutionize your organization.

The Ideal Shower Duration for Healthy Skin: Tips from a Dermatologist

Discover the optimal shower length for maintaining healthy skin and tips from a dermatologist to prevent dryness.