Spring Boot Common Module for Microservices: Best Practices
One of the most common failure patterns in microservice ecosystems has nothing to do with distributed systems, networking, or scalability.
It begins with copy-paste.
A team creates a User Service and implements a global exception handler. A second service needs similar behavior, so the code is copied. A third service needs localization support, so that code is copied as well. Over time, every service develops its own slightly different implementation of response handling, validation, logging, error formatting, localization, and utility functions.
Initially, this duplication appears harmless.
As the platform grows, it becomes one of the most expensive forms of technical debt.
A bug fix requires updates across multiple repositories. Error responses become inconsistent. Localization behaves differently across services. Engineering standards begin to drift.
The Common Module exists to prevent this outcome.
Rather than copying infrastructure code across services, shared concerns are centralized into a versioned library that can evolve independently while enforcing consistency across the platform.
In this article, we’ll examine how a centralized Common Module reduces duplication, improves maintainability, and establishes engineering standards across a Spring Boot microservices ecosystem.
Error handling is often implemented differently in every service.
This creates several problems:
Inconsistent error formats
Duplicate code
Difficult debugging
Different localization behavior
The GlobalExceptionHandler centralizes these concerns.
Rather than forcing every service to implement identical exception handling logic, services inherit a common implementation and extend it only when necessary.
This ensures that:
Error structures remain consistent
Localization behaves identically
Logging standards remain aligned
while still allowing service-specific customization.
public class GlobalExceptionHandler
extends ResponseEntityExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity
How to Use GlobalExceptionHandler in Other Services
In this setup, the GlobalExceptionHandler is intentionally not annotated with @RestControllerAdvice. This gives you better flexibility and control. Instead of letting Spring auto-detect it, we design it to be reused through inheritance.
Add the Dependency to Your Service Next, include this shared module in the service where you want to use it.
Many applications introduce localization only after international users arrive.
Retrofitting localization into an existing platform is often painful because messages become scattered across controllers, services, and exception classes.
The Common Module treats localization as a first-class capability from the beginning.
By centralizing locale resolution and message source configuration, every service automatically gains multilingual support without implementing its own localization infrastructure.
This allows teams to add additional languages with minimal application changes while maintaining consistent user experiences across the platform.
@Configuration
public class I18nConfig implements WebMvcConfigurer {
private static final List SUPPORTED_LOCALES =
Arrays.asList(new Locale("en"), new Locale("fr"));
@Bean
public LocaleResolver localeResolver() {
return new AcceptHeaderLocaleResolver() {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String headerLang = request.getHeader("Accept-Language");
return headerLang == null || headerLang.isEmpty()
? Locale.getDefault()
: Locale.lookup(
Locale.LanguageRange.parse(headerLang),
SUPPORTED_LOCALES);
}
};
}
}
Centralized Distributed Tracing
A major benefit of the Common Module is centralizing distributed tracing. By including `micrometer-tracing-bridge-brave` and `zipkin-reporter-brave`, we ensure all microservices emit consistent trace headers and spans.
To bypass missing or complex Spring Boot autoconfigurations, the Common Module includes `TracingManualConfig`:
@Configuration
public class TracingManualConfig {
@Bean
@ConditionalOnMissingBean
public DefaultTracingObservationHandler tracingObservationHandler(Tracer tracer) {
return new DefaultTracingObservationHandler(tracer);
}
// ... configures Zipkin sender, Brave Tracing, and Micrometer Tracer
}
Any service depending on the Common Module will automatically start reporting distributed traces to Zipkin without writing custom configurations in every service.
Versioning Strategy
Shared libraries introduce different operational challenges upgrades.
Every service consuming in the Common Module must remain compatible with new releases.
For this reason:
Breaking changes should be minimized.
Semantic versioning should be followed.
Backward compatibility should be preserved where possible.
Typical versioning approach:
Major Version: Breaking changes.
Minor Version: New features.
Patch Version: Bug fixes.
This allows services to upgrade predictably while avoiding unexpected runtime behavior.
Production Considerations
Dependency Governance Every shared library becomes a platform of dependency.
Changes must be reviewed carefully because they can impact multiple services simultaneously.
Backward Compatibility Avoid introducing changes that require all services to upgrade immediately.
Incremental adoption is far safer.
Release Management Treat shared libraries with the same rigor as production services.
Changes should undergo:
Code review
Testing
Version tagging
Release documentation
Documentation Shared modules are only valuable when engineers understand how to use them.
Every reusable component should include:
Usage examples
Extension patterns
Migration notes
Architectural Benefits
The Common Module provides benefits beyond code reuse.
Consistency: All services behave similarly.
Faster Development: New services start with established standards.
Easier Maintenance: Bug fixes occur once instead of multiple times.
Improved Governance: Architectural standards become enforceable rather than optional.
Better Developer Experience: Teams focus on business capabilities rather than rebuilding infrastructure repeatedly.
Conclusion
The Common Module is rarely the most visible component in a microservices platform, but it is often one of the most important.
Without shared standards, microservice ecosystems gradually drift toward inconsistency. Error handling differs between services, response structures evolve independently, and infrastructure concerns are repeatedly reimplemented.
By centralizing cross-cutting concerns into a versioned shared library, teams establish a foundation of consistency without sacrificing service independence.
The result is not merely less code duplication. The result is a platform that behaves predictably, scales more effectively, and remains maintainable as the number of services and engineering teams grows.
In many ways, the Common Module acts as the architectural backbone of the platform, ensuring that every service speaks the same language even as they evolve independently.
Why not simply copy code between services? Copying infrastructure code appears faster initially but creates long-term maintenance challenges. Bug fixes, enhancements, and standards of enforcement become increasingly difficult as duplication grows.
Can a Common Module become too large? Yes.
Many organizations eventually create “God Libraries” containing unrelated functionality. Shared modules should focus on infrastructure concerns rather than business capabilities.
Should domain entities be shared?
Generally, not.
Domain ownership should remain within individual services.
How do services adopt new Common Module features?
Through versioned releases and dependency upgrades.
This allows teams to adopt changes incrementally rather than requiring platform-wide updates.
Is a Common Module required in every microservices architecture?
Not necessarily.
Smaller systems may not benefit immediately. As the number of services grows, however, shared infrastructure becomes increasingly valuable.
Bharat Chaudhari is a Java Backend Engineer with 5+ years of experience building scalable microservices using Spring Boot. He specializes in distributed systems, event-driven architecture, Spring Security, Kafka, PostgreSQL, Redis, Docker, and Kubernetes, with a passion for building production-ready backend platforms.
Delivering Engineering Excellence Across Global Markets
India
USA
UAE
Europe
Singapore
Australia
Need Help Building Enterprise Spring Boot Microservices?
We specialize in designing secure, scalable, and event-driven microservices using Spring Boot, Kafka, PostgreSQL, Redis, OAuth2/JWT, Docker, Kubernetes, and cloud-native best practices for enterprise applications.
For 12+ years, Inexture has helped global enterprises design, build, modernize, and scale secure, high-performance digital platforms. We combine deep engineering expertise with cloud, enterprise systems, backend architecture, mobile, AI, and user centric design delivering solutions that make businesses future ready.