Explore Developer Center's New Chatbot! MongoDB AI Chatbot can be accessed at the top of your navigation to answer all your MongoDB questions.

MongoDB Developer
Java
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Languageschevron-right
Javachevron-right

Microservices Architecture: Deploy Locally With Java, Spring, and MongoDB

Maxime Beugnet4 min read • Published Aug 29, 2024 • Updated Aug 29, 2024
SpringDockerMongoDBJava
FULL APPLICATION
Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty

Introduction

"Microservices are awesome and monolithic applications are evil."
If you are reading this article, you have already read that a million times, and I'm not the one who's going to tell you otherwise!
In this post, we are going to create a microservices architecture using MongoDB.

TL;DR

The source code is available in these two repositories.
The README.md files will help you start everything.
1git clone git@github.com:mongodb-developer/microservices-architecture-mongodb.git
2git clone git@github.com:mongodb-developer/microservices-architecture-mongodb-config-repo.git

Microservices architecture

We are going to use Spring Boot and Spring Cloud dependencies to build our architecture.
Here is what a microservices architecture looks like, according to Spring:
Spring Microservices Architecture
We need several services to run this project. Let's talk about them one by one.
As you read this post, I suggest that you follow the instructions in the README.md file and start the service related to each section.

Config server

The first service that we need is a configuration server.
This service allows us to store all the configuration files of our microservices in a single repository so our configurations are easy to version and store.
The configuration of our config server is simple and straight to the point:
1spring.application.name=config-server
2server.port=8888
3spring.cloud.config.server.git.uri=${HOME}/Work/microservices-architecture-mongodb-config-repo
4spring.cloud.config.label=main
It allows us to locate the git repository that stores our microservices configuration and the branch that should be used.
Note that the only "trick" you need in your Spring Boot project to start a config server is the @EnableConfigServer annotation.
1package com.mongodb.configserver;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5import org.springframework.cloud.config.server.EnableConfigServer;
6
7@EnableConfigServer
8@SpringBootApplication
9public class ConfigServerApplication {
10 public static void main(String[] args) {
11 SpringApplication.run(ConfigServerApplication.class, args);
12 }
13}

Service registry

A service registry is like a phone book for microservices. It keeps track of which microservices are running and where they are located (IP address and port). Other services can look up this information to find and communicate with the microservices they need.
A service registry is useful because it enables client-side load balancing and decouples service providers from consumers without the need for DNS.
Again, you don't need much to be able to start a Spring Boot service registry. The @EnableEurekaServer annotation makes all the magic happen.
1package com.mongodb.serviceregistry;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
6
7@SpringBootApplication
8@EnableEurekaServer
9public class ServiceRegistryApplication {
10 public static void main(String[] args) {
11 SpringApplication.run(ServiceRegistryApplication.class, args);
12 }
13}
The configuration is also to the point:
1spring.application.name=service-registry
2server.port=8761
3eureka.client.register-with-eureka=false
4eureka.client.fetch-registry=false
The last two lines prevent the service registry from registering to itself and retrieving the registry from itself.

API gateway

The API gateway service allows us to have a single point of entry to access all our microservices. Of course, you should have more than one in production, but all of them will be able to communicate with all the microservices and distribute the workload evenly by load-balancing the queries across your pool of microservices.
Also, an API gateway is useful to address cross-cutting concerns like security, monitoring, metrics gathering, and resiliency.
When our microservices start, they register themselves to the service registry. The API gateway can use this registry to locate the microservices and distribute the queries according to its routing configuration.
1server:
2 port: 8080
3
4spring:
5 application:
6 name: api-gateway
7 cloud:
8 gateway:
9 routes:
10 - id: company-service
11 uri: lb://company-service
12 predicates:
13 - Path=/api/company/**,/api/companies
14 - id: employee-service
15 uri: lb://employee-service
16 predicates:
17 - Path=/api/employee/**,/api/employees
18
19eureka:
20 client:
21 register-with-eureka: true
22 fetch-registry: true
23 service-url:
24 defaultZone: http://localhost:8761/eureka/
25 instance:
26 hostname: localhost
Note that our API gateway runs on port 8080.

MongoDB microservices

Finally, we have our MongoDB microservices.
Microservices are supposed to be independent of each other. For this reason, we need two MongoDB instances: one for each microservice.
Check out the README.md file to run everything.
Note that in the configuration files for the company and employee services, they are respectively running on ports 8081 and 8082.
1spring.data.mongodb.uri=${MONGODB_URI_1:mongodb://localhost:27017}
2spring.threads.virtual.enabled=true
3management.endpoints.web.exposure.include=*
4management.info.env.enabled=true
5info.app.name=Company Microservice
6info.app.java.version=21
7info.app.type=Spring Boot
8server.port=8081
9eureka.client.register-with-eureka=true
10eureka.client.fetch-registry=true
11eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
12eureka.instance.hostname=localhost
1spring.data.mongodb.uri=${MONGODB_URI_2:mongodb://localhost:27018}
2spring.threads.virtual.enabled=true
3management.endpoints.web.exposure.include=*
4management.info.env.enabled=true
5info.app.name=Employee Microservice
6info.app.java.version=21
7info.app.type=Spring Boot
8server.port=8082
9eureka.client.register-with-eureka=true
10eureka.client.fetch-registry=true
11eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
12eureka.instance.hostname=localhost
Note that the two microservices are connected to two different MongoDB clusters to keep their independence. The company service is using the MongoDB node on port 27017 and the employee service is on port 27018.
Of course, this is only if you are running everything locally. In production, I would recommend to use two clusters on MongoDB Atlas. You can overwrite the MongoDB URI with the environment variables (see README.md).

Test the REST APIs

At this point, you should have five services running:
  • A config-server on port 8888
  • A service-registry on port 8761
  • An api-gateway on port 8080
  • Two microservices:
    • company-service on port 8081
    • employee-service on port 8082
And two MongoDB nodes on ports 27017 and 27018 or two MongoDB clusters on MongoDB Atlas.
If you start the script 2_api-tests.sh, you should get an output like this.
1DELETE Companies
22
3DELETE Employees
42
5
6POST Company 'MongoDB'
7POST Company 'Google'
8
9GET Company 'MongoDB' by 'id'
10{
11 "id": "661aac7904e1bf066ee8e214",
12 "name": "MongoDB",
13 "headquarters": "New York",
14 "created": "2009-02-11T00:00:00.000+00:00"
15}
16
17GET Company 'Google' by 'name'
18{
19 "id": "661aac7904e1bf066ee8e216",
20 "name": "Google",
21 "headquarters": "Mountain View",
22 "created": "1998-09-04T00:00:00.000+00:00"
23}
24
25GET Companies
26[
27 {
28 "id": "661aac7904e1bf066ee8e214",
29 "name": "MongoDB",
30 "headquarters": "New York",
31 "created": "2009-02-11T00:00:00.000+00:00"
32 },
33 {
34 "id": "661aac7904e1bf066ee8e216",
35 "name": "Google",
36 "headquarters": "Mountain View",
37 "created": "1998-09-04T00:00:00.000+00:00"
38 }
39]
40
41POST Employee Maxime
42POST Employee Tim
43
44GET Employee 'Maxime' by 'id'
45{
46 "id": "661aac79cf04401110c03516",
47 "firstName": "Maxime",
48 "lastName": "Beugnet",
49 "company": "Google",
50 "headquarters": "Mountain View",
51 "created": "1998-09-04T00:00:00.000+00:00",
52 "joined": "2018-02-12T00:00:00.000+00:00",
53 "salary": 2468
54}
55
56GET Employee 'Tim' by 'id'
57{
58 "id": "661aac79cf04401110c03518",
59 "firstName": "Tim",
60 "lastName": "Kelly",
61 "company": "MongoDB",
62 "headquarters": "New York",
63 "created": "2009-02-11T00:00:00.000+00:00",
64 "joined": "2023-08-23T00:00:00.000+00:00",
65 "salary": 13579
66}
67
68GET Employees
69[
70 {
71 "id": "661aac79cf04401110c03516",
72 "firstName": "Maxime",
73 "lastName": "Beugnet",
74 "company": "Google",
75 "headquarters": "Mountain View",
76 "created": "1998-09-04T00:00:00.000+00:00",
77 "joined": "2018-02-12T00:00:00.000+00:00",
78 "salary": 2468
79 },
80 {
81 "id": "661aac79cf04401110c03518",
82 "firstName": "Tim",
83 "lastName": "Kelly",
84 "company": "MongoDB",
85 "headquarters": "New York",
86 "created": "2009-02-11T00:00:00.000+00:00",
87 "joined": "2023-08-23T00:00:00.000+00:00",
88 "salary": 13579
89 }
90]
Note that the employee service sends queries to the company service to retrieve the details of the employees' company.
This confirms that the service registry is doing its job correctly because the URL only contains a reference to the company microservice, not its direct IP and port.
1private CompanyDTO getCompany(String company) {
2 String url = "http://company-service/api/company/name/";
3 CompanyDTO companyDTO = restTemplate.getForObject(url + company, CompanyDTO.class);
4 if (companyDTO == null) {
5 throw new EntityNotFoundException("Company not found: ", company);
6 }
7 return companyDTO;
8}

Conclusion

And voilà! You now have a basic microservice architecture running that is easy to use to kickstart your project.
In this architecture, we could seamlessly integrate additional features to enhance performance and maintainability in production. Caching would be essential, particularly with a potentially large number of employees within the same company, significantly alleviating the load on the company service.
The addition of a Spring Cloud Circuit Breaker could also improve the resiliency in production and a Spring Cloud Sleuth would help with distributed tracing and auto-configuration.
If you have questions, please head to our Developer Community website where the MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.
Top Comments in Forums
There are no comments on this article yet.
Start the Conversation

Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Tutorial

Spring Data Unlocked: Getting Started With Java and MongoDB


Nov 11, 2024 | 5 min read
Tutorial

Handle Time Series Data with MongoDB


Nov 19, 2024 | 13 min read
Tutorial

Single-Collection Designs in MongoDB with Spring Data (Part 1)


Sep 09, 2024 | 10 min read
Article

Java vs Kotlin: Different Syntax, Same Possibilities


Nov 25, 2024 | 5 min read
Table of Contents
  • Introduction