Microservice Architecture with Spring Boot
When it comes to scaling your applications to a larger level(may be expecting a considerable number of users viewing/using it), microservice architecture comes to our rescue. There are a plethora of configurations which can be done in order to load balance, route, secure our applications(among many-many other options ofcourse!).
Well, as a developer myself I have learnt that when it comes to web scaling, “Java” is the king!. So today I will be demonstrating a POC(Proof Of Concept) of microservice architecture using SpringBoot with the help of a small project which I built myself.
It has been an year when I was trained to work using this architecture and I must say it’s so much fun to explore the vivid array of tasks/end-goals which can be achieved using this
Let’s first peek into the following diagram which depicts the architecture of our application which we will be building.
In order to simplify things, let me jot down the tasks here in terms of user-stories and we’ll look into each one of those step-by-step.
- Understanding the architecture.
- Generate a multimodule parent project in Spring Boot.
- Add the required dependencies in the parent pom file.
- Generate each individual module except the UI(or in our case, each microservice)as a spring starter project and adding the required dependencies for it.
- Preparing the UI as an angular project.
- Creating a separate project for the UI in spring boot (to package the angular application into Spring)
- Modifying the routes in the gateway to run all the applications.
- Voila! we’re done!
So without further ado, let’s dive into the steps and check them out in greater detail. My entire code is present on github(the link of which will be present a few lines below), but I will putting code snippets in between the steps wherever I feel, necessary.
Understanding the architecture
Now, to understand the entire architecture I have tried my best to simplify the namings. The names mentioned in the boxes are the names of our microservices and the numbers in the brackets represent the ports on which they will be running in the end. Now if you look at the architecture, I think it’s pretty clear that, the Dashboard Service, Pricing service and the UI are the parts which will contain all of(if not all, most of) the business logic of our application. So what are the other two, namely, API Gateway and Discovery Service? Well, in simple words, the API gateway is the entry point to our application. Any request for any resource/endpoint will have to pass through the gateway in order to get routed to the respective service(microservice). See, this is the way to separate/break our application into different parts. Now if someday, let’s suppose we have to modify something on our dashboard-service, we need not touch/modify any other microservices at all! Separation on concerns after all! For the gateway we will be using Spring Cloud Gateway which is non-blocking as compared to its old counter part Zuul. To learn more about the differences between the two, click here.
Coming to the second service, i.e Discovery Service a.k.a Eureka Discovery Service. According to google, Eureka Server is an application that holds the information about all client-service applications. Every Micro service will register into the Eureka server and Eureka server knows all the client applications running on each port and IP address. We definitely need it because we need a “monitor” which will basically be able to tell us, which microservices are up and running and which are down(if any). So let’s get to the next step.
Generate a multimodule parent project in Spring Boot
So first of all we need to have a parent project and subsequently, the different microservices (or modules) are going to be inside it. This comes in handy when we have to include certain dependencies and if we are containerizing our entire application(we can keep the docker-compose file here). The command for generating the parent is :
mvn archetype:generate -DartifactId=<artifactID> -DgroupId=<group_id> -Dversion=1.0.0-SNAPSHOT
(Replace the content withing the angle brackets with your own group id and artifact id).
Add the required dependencies in the parent pom file.
Now above command will provide you a whole bunch of options, make sure you select the pom root option(you could search your terminal for this). Finally after it has been generated and open your project in intellij. You can find my final code here(but I will be providing code snippets along the way too).
So finally my parent pom looked like this(along with other dependencies included, make sure to include them as per your requirements)
Now the “modules” section in the above code snippet represents all the microservices in our project. Moving on further,
Generate each individual module except the UI(or in our case, each microservice)as a spring starter project and adding the required dependencies for it
Now proceed to https://start.spring.io to create individual spring boot projects(except the UI). Here is an example:
I will be sharing three important files for each project here namely, the pom file, the application.properties and the controller(APIs). Here’s an important note though: for gateway and eureka server, we need the respective dependencies, but for dashboard and pricing microservices, we only need, web and eureka client dependencies and don’t forget to add the module in the parent pom after including each project into our parent folder. Let’s peek into the afore-mentioned files one-by-one.
The pom.xml for eureka server:
The application.properties for eureka-server:
The main file for eureka-server:
As you can see from the code snippets, we are doing the bare minimum setups for a microservice architecture. Now let’s create our gateway application. Head over to spring starter website and add the dependencies. Download and unzip the folder and copy it into the parent folder.
The pom.xml for gateway:
The application.properties file for gateway:
Note: Don’t worry about the routes. They will be clear once we have finished setting up all other microservices. We don’t have to run anything yet. I did create a SimpleFilter.java file in case we need some custom configuration for our routes but not to worry, we won’t be implementing anything as such in this project.
The main file for gateway:
Now if you noticed before the example screenshot for the spring initializer website contained the dependencies needed(web and eureka client) for the dashboard application. Both these microservices (dashboard and pricing) are identical in terms for the dependencies needed. The only thing which differs is — the two different routes we are exposing(which is no rocket science if you’re already familiar with spring). So in order to prevent this article from getting too verbose, I will only be sharing the controller files from both these applications.
Controller for Dashboard Service:
Controller for the Pricing Service:
Now our backend is ready! 😄 Since we have not containerized our application, we will have to start it in a specific order. (before that make sure to remove the micro-ui module from the parent pom and the default route in gateway’s application.properties as we haven’t prepared our frontend yet)
Discovery -> Gateway -> Dashboard -> Pricing
Now if you use postman to send get requests to the created routes, we will get the following responses.
Response from: http://localhost:8080/dashboard-service/api/v1/details
Response from: http://localhost:8080/pricing-service/api/v1/pricing
Now let’s go ahead and create the frontend part.
Preparing the UI as an angular project
What we need in the frontend is two components which will display the above responses. Honestly, if you managed to setup until the last step and got everything running, that’s a very good step, because setting up the frontend and getting it running is going to be way more easy. So just prepare a basic angular project with angular-cli and make sure you create two components along with default app component. If you’re not a frontend loving guy like me, feel free to browse the explanation and in the end, it will all make sense 😎
Creating a separate project for the UI in spring boot (to package the angular application into Spring)
We need to understand a few things here. In order to make it simple I will jot them down as different points.
- We’ll be packaging the angular application inside a spring-boot application.
- We’ll be doing this in order to keep our UI microservice “behind” the API gateway, in order to avoid having CORS errors(because as mentioned earlier and depicted in the first architecture diagram, our gateway will be the single entry point for our application).
- First of all make sure to use hash-routing by providing setting the “useHash” property to true in angular.json file(useHash: true, refer code). This is done so that our gateway service can resolve the routes, when they’re changed from the frontend without throwing a “Whitelabel Error”.
- So create a spring boot project and only provide the web and eureka client dependencies. Now copy the angular project into this spring boot project(after importing it in the project-root directory, and make sure to remove node_modules folder before you copy).
- Now we have to serve the frontend from within this spring application. We will do that by adding a maven goal. First build your frontend with the help of “ng build” command. (Note: Angular 8 onwards, whenever we build an angular project, instead of rendering the files inside the “dist” folder directly, it renders into the “dist/<project-name>” folder, it’s upto you how you like it, I prefer everything to be inside the dist folder directly, so I have modified my code accordingly, don’t worry, you can google to find out how to do it or just refer my code).
- The following maven goal (it’s a plugin to be more specific, which is added inside the <build> goal)needs to be added in the pom file then:
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/src/main/resources/static</outputDirectory>
<resources>
<resource>
<directory>${basedir}/frontend/dist</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
- As you can see, after implementing the above plugin when we build our project, it will copy all the files in the dist folder to the static folder of our spring boot project(refer my code side-by-side to get more clarity). So now our UI is packaged inside the spring microservice!
Modifying the routes in the gateway to run all the applications
Now keep the routes in the gateway as mentioned previously. The below mentioned lines,
spring.cloud.gateway.routes[2].id=micro-ui
spring.cloud.gateway.routes[2].uri=http://localhost:8085
spring.cloud.gateway.routes[2].predicates[0]=Path=/**
help us to route the default “/” route to render our frontend(in our case, I named it, micro-ui). And now after running all the applications in the previously mentioned order, run the UI application too. If you head over to eureka’s home page(localhost:8761), you can see that all the microservices are register to eureka and are up and running now. Like this:
Now let’s head over and check out the frontend part:
Yay! 😃 All our microservices are now running and if you notice the browser’s console while changing routes, there are no nasty CORS errors. It’s because of our architecture. All the microservices are behind the API-Gateway.
I hope this article was helpful in providing an insight into setting up a basic microservice architecture. Please note that, though many projects in production use this architecture today, but when it comes to a business critical apps/websites, there are a lot more configurations and setup that need to be done. I learnt this during my training last year and decided to document this simple application after lots of experimenting and reading of course. Also from my experience I can say that, springboot can be a pain for new learners because sometimes the errors are difficult to comprehend. I suggest to take help from some seniors/mentors/connections whom you might know. As the saying goes “the best way to learn something is, to learnt from the experts themselves”. I was lucky enough to have amazing mentors during my training. Hope you liked this article and if you did, show some appreciation by giving it a few claps and share it with your peers who you think might need it.
Update: I have updated this article to contain shell scripts. You can find 3 files in the root of the repository named, start_services.sh, stop_services.sh and restart_services.sh. They help us to start/stop/restart any of the above microservices as and when needed. This is especially helpful if the no. of microservices increases — as in that case we would need more time just to start/stop a particular microservice(or multiple microservices) on which we are working at any time.
Happy learning! 😎 😎