Have you ever wondered what does it take to refactor an existing Java EE monolithic application to a microservices-based one?
This blog explains how a trivial shopping cart example was converted to microservices-based application, and what are some of the concerns around it. The complete code base for monolithic and microservices-based application is at: github.com/arun-gupta/microservices.
Read on for full glory!
Java EE Monolith
A Java EE monolithic application is typically defined as a WAR or an EAR archive. The entire functionality for the application is packaged in a single unit. For example, an online shopping cart may consist of User, Catalog, and Order functionalities. All web pages are in root of the application, all corresponding Java classes are in the WEB-INF/classes
directory, resources in WEB-INF/classes/META-INF
directory.
Lets assume that your monolith is not designed as a distributed big ball of mud and the application is built following good software architecture. Some of the common rules are:
- Separation of concerns, possibly using Model-View-Controller
- High cohesion and low coupling using well-defined APIs
- Don’t Repeat Yourself (DRY)
- Interfaces/APIs and implementations are separate, and following Law of Demeter. Classes don’t call other classes directly because they happen to be in the same archive
- Using Domain Driven Design to keep objects related to a domain/component together
- YAGNI or You Aren’t Going to Need It: Don’t build something that you don’t need now
Here is how a trivial shopping cart monolithic WAR archive might look like:
This monolithic application has:
- Web pages, such as
.xhtml
files, for User, Catalog, and Order component, packaged in the root of the archive. Any CSS and JavaScript resources that are shared across different webpages are also packaged with these pages. - Classes for the three components are in separate packages in
WEB-INF/classes
directory. Any utility/common classes used by multiple classes are packed here as well. - Configuration files for each component are packaged in
WEB-INF/classes/META-INF
directory. Any config files for the application, such aspersistence.xml
andload.sql
to connect and populate the data store respectively, are also packaged here.
It has the usual advantages of well-known architecture, IDE-friendly, easy sharing, simplified testing, easy deployment, and others. But also comes with disadvantages such as limited agility, obstacle for continuous delivery, “stuck” with a technology stack, growing technical debt, and others.
Even though microservices are all the raze these days, but monoliths are not bad. Even those that are not working for you may not benefit much, or immediately, from moving to microservices. Other approaches, such as just better software engineering and architecture, may help. Microservices is neither a free lunch or a silver bullet and requires significant investment to be successful such as service discovery, service replication, service monitoring, containers, PaaS, resiliency, and a lot more.
don’t even consider microservices unless you have a system that’s too complex to manage as a monolith.
Microservice Architecture for Java EE
Alright, I’ve heard about all of that but would like to see a before/after, i.e. how a monolith code base and how a refactored microservice codebase looks like.
First, lets look at the overall architecture:
The key pieces in this architecture are:
- Application should be functionally decomposed where User, Order, and Catalog components are packaged as separate WAR files. Each WAR file should have the relevant web pages (#15), classes, and configuration files required for that component.
- Java EE is used to implement each component but there is no long term commitment to the stack as different components talk to each other using a well-defined API (#14).
- Different classes in this component belong to the same domain and so the code is easier to write and maintain. The underlying stack can also change, possibly keeping technical debt to a minimum.
- Each archive has its own database, i.e. no sharing of data stores. This allows each microservice to evolve and choose whatever type of datastore – relational, NoSQL, flat file, in-memory or some thing else – is most appropriate.
- Each component will register with a Service Registry. This is required because multiple stateless instances of each service might be running at a given time and their exact endpoint location will be known only at the runtime (#17).Netflix Eureka, Etcd, Zookeeper are some options in this space (more details).
- If components need to talk to each other, which is quite common, then they would do so using a pre-defined API. REST for synchronous or Pub/Sub for asynchronous communication are the common means to achieve this.In our case, Order component discovers User and Catalog service and talks to them using REST API.
- Client interaction for the application is defined in another application, Shopping Cart UI in our case. This application mostly discover the services from Service Registry and compose them together. It should mostly be a dumb proxy where the UI pages of different components are invoked to show the interface (#18).A common look-and-feel can be achieved by providing a standard CSS/JavaScript resources.
This application is fairly trivial but at least highlights some basic architectural differences.
Monolith vs Microservice
Some of the statistics for the monolith and microservices-based applications are compared below:
Characteristic | Monolith | Microservice |
---|---|---|
Number of archives | 1 |
5
|
Web pages | 8 | 8 (see below) |
Configuration Files |
4
|
3 per archive
|
Class files | 12 |
26
|
Total archive size | 24 KB | ~52 KB (total) |
Code base for the monolithic application is at: github.com/arun-gupta/microservices/tree/master/monolith/everest
Code base for the microservices-enabled application is at: github.com/arun-gupta/microservices/tree/master/microservice
Issues and TODOs
Here are the issues encountered, and TODOs, during refactoring of the monolith to a microservices-based application:
- Java EE already enables functional decomposition of an application using EAR packaging. Each component of an application can be packaged as a WAR file and be bundled within an EAR file. They can even share resources that way. Now that is not a true microservices way, but this could be an interim step to get you started. However, be aware that
@FlowScoped
bean is not activated in an EAR correctly (WFLY-4565). - Extract all template files using JSF Resource Library Templates.
- All web pages are currently in
everest
module but they should live in each component instead (#15). - Resource Library Template should be deployed at a central location as opposed to packaged with each WAR file (#16).
- All web pages are currently in
- Breakup monolithic database into multiple databases require separate
persistence.xml
and DDL/DML scripts for each application. Similarly, migration scripts, such as using Flyway, would need to be created accordingly. - A REST interface for all components, that need to be accessed by another one, had to be created.
- UI is still in a single web application. This should instead be included in the decomposed WAR (#15) and then composed again in the dumb proxy. Does that smell like portlets?
- Deploy the multiple WAR files in a PaaS (#12)
- Each microservice should be easily deployable in a container (#6)
Here is the complete list of classes for the monolithic application:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
./target/classes/org/javaee7/wildfly/samples/everest/cart/Cart.class
./target/classes/org/javaee7/wildfly/samples/everest/cart/CartItem.class
./target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItem.class
./target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemBean.class
./target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemType.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/Order.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderBean.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderItem.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/Shipping.class
./target/classes/org/javaee7/wildfly/samples/everest/uzer/Uzer.class
./target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerBean.class
./target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerItem.class
|
Here is the complete list of classes for the microservices-based application:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/ApplicationConfig.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItem.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemREST.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemType.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/ServiceRegistration.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/cart/Cart.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/cart/CartItem.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogBean.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItem.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/Order.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderBean.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderItem.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/Shipping.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscovery.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscoveryStatic.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscoveryURI.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscoveryZooKeeper.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerBean.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerItem.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/ApplicationConfig.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/Order.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/OrderItem.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/OrderREST.class
./user/target/classes/org/javaee7/wildfly/samples/everest/uzer/ApplicationConfig.class
./user/target/classes/org/javaee7/wildfly/samples/everest/uzer/UserREST.class
./user/target/classes/org/javaee7/wildfly/samples/everest/uzer/Uzer.class
|
Once again, the complete code base is at github.com/arun-gupta/microservices.
Future Topics
Some of the future topics in this series would be:
- Are containers required for microservices?
- How do I deploy multiple microservices using containers?
- How can all these services be easily monitored?
- A/B Testing
- Continuous Deployment using microservices and containers
What else would you like to see?
Enjoy!
Thanks, nice overview picture.
Nice idea! (and first post :))
Another important thing to keep in mind, I think, is how developers can work efficiently with applications based on microservices. Indeed, for now it’s easy to develop and test on your machine with your IDE and one big WAR/EAR but how to do that with several databases, WAR, API….especially if you develop only on one or two components?
Yeah, I think that you know what I mean: Docker Compose to the rescue
So, I think that there are some things to improve on the developer side with microservices architecture.
Last thing, don’t you think that it would be better to handle your github repository with 2 branches instead of one master branch? Maybe it will be easiest to compare the codebase between monolithic and microservices architecture.
Thanks.
Maxime.
@mgreau
Hi Arun,
very great article.
For me the best is your linked GitHub project to see your example!
I have a question to your architecture milestones:
What do you have in mind for the following questions:
– Is the communication done directly or via the service registry?
– Is there a client API for the DTO’s available ?
I’m looking forward to see how this example project will grow, in special for this point:
A REST interface for all components, that need to be accessed by another one, had to be created.
Cheers,
Matthias
Hi Arun,
Thanks for the post. This architecture seems to push higher level domain/biz logic that involves dependencies across decomposed components up into the UI layer.
For example, in a traditional monolithic approach I might @Inject or @EJB a dependency from User into Order in order, or compose an EJB controller class that combines the two to express some biz logic that involves both concerns.
In the microservice approach here it seems I would have to compose that dependency in the UI layer. If that layer is expressed with pieces that are distributed and isolated with the decomposed WAR and reassembled with a proxy how can you do that and still maintain this strict separation? Perhaps the approach would be to have a 4th component that contains these aggregates?
Noah,
UI layer in this application needs to be further refactored into the respective component. This makes the front-end layer as mostly the composition layer, instead of performing any business logic. This is already filed as an issue at: https://github.com/arun-gupta/microservices/issues/18 and https://github.com/arun-gupta/microservices/issues/15
Different microservices design patterns, such as the aggregate and the proxy, are also discussed at:
http://blog.arungupta.me/microservice-design-patterns/
Matthias,
*All* service discovery needs to happen using a similar mechanism. This follows the DRY principle and allows to easily manage the registration and discovery code at one place. This is already shown at:
https://github.com/arun-gupta/microservices/blob/master/microservice/everest/src/main/java/org/javaee7/wildfly/samples/everest/catalog/CatalogBean.java#L28
Maxime,
Agreed on simplified tooling aspect as well.
Monolithic and Microservice applications are in two intuitive directory names “monolith” and “microservice”. What would be the purpose of keeping them in two separate branches?
First of all I want to say that your blog is good. In this blog you show the people about the Java functions, but I am exited to know more about Monolith. Many Best Java Training Course institutes should give advance class of Java to know more about Java.
The drawback on the micro service solution is that Order references a Catalog Item
and there is no foreign key between them (2 differents schema). So data integrity is handled by the application, not the database system itself.
But is this a real flaw ?
Sylvain,
Think about how you want to re-composing your microservice with the right set of schema in that case?
Good post! Couple of questions though
1. when you say “Application should be functionally decomposed where User, Order, and Catalog components are packaged as separate WAR files. Each WAR file should have the relevant web pages (#15), classes, and configuration files required for that component.”, it sounds more a like a portal/portlet model where each portlet has its UI, backend code etc. Is your idea of Microservice more like a portlet?
2. Also, when you say “Client interaction for the application is defined in another application, Shopping Cart UI in our case.”, How does this UI work with User, Order and Catalog component who have their own pages?
Thanks for your time
JK
JK,
This sample application is canonical and that’s why it may feel like that. In real world, there will be several single-purpose and headless microservices that may be serving in the background to meet the business need. So the approach has some similarities but still quite different.
Consistent UI works by adopting the same CSS stylesheets for all the components.
Thanks for the article.
I didn’t see any discussion about how to secure microservices?
Even in scenarios where services are behind proxies and not publicly accessible, you still want them secured. You don’t want malware getting inside and having uncontrolled access to those REST APIs.
How would you tackle this?
Clint,
There is nothing special about securing microservices. Typically, end-to-end security is achieved using HTTPS, or using standard REST practices.
Hi Arun,
> There is nothing special about securing microservices. Typically, end-to-end security is
> achieved using HTTPS, or using standard REST practices.
Could you recommended references for securing REST using ‘standard REST practices’? Are the REST services in your shopping cart app secured?
thanks.
Clint,
They are not secured at this time, but eventually will make them so.
I have two questions..First is why is the Order domain object repeated under two contexts(/everest and /order) and my second question is how to model these microservices in case one is dependent on another..example: find the orders for a given user? In case of monolithic app, url could be modelled as /user/{id}/orders and this would make an in-memory service call to get the orders but in this case should we make a remote service call to fetch or is there a better way to do it?
Anush,
There is no sharing of code between two microservices and thus the code needs to be duplicated.
Each microservice will invoke each other, typically, using REST APIs. So the User microservice can invoke the the Order to retrieve the results.
Arun, thank you for your leadership.
I am new to microservices and begnining to explore. My question is about the database decomposition from monolithic to miroservices style. How do you handle the entity relationship when segregating the entities to their respective domains (eg. Order, User, Catalog in this case)? Or are you going to duplicate all entities in all services?
Hi Arun,
First of all, I want to thank you for your blog and your resources, they are so goods.
I am a master student and I am new in the microservices world, but my current work is about the use of microservices and docker services. How can I deploy this example with docker? This would be a great help for me in this moment because your approach in the use of Java, Microservices and Docker is one of the most important guide of my thesis. In fact, I am reading your book Docker for Java Developers.
Thank you very much!
Hi Arun,
This blog post is really awesome. I am new to microservices and just learning the concepts for one of my case study.
At most of the tutorials I found that microservices are typically in JAR instead of WAR.
I am confused!!
For a webapplication say for example ERP; is it a good idea to design microservices for modules and have one big module to render the UI ?
E.G – m1, m2, m3 are all JARs and we have m4 responsible for UI. So will it be best approach to have 3 JARs and one WAR microservices in this ERP.
One important drawback with Microservices is that REST is not transactional.
So if you system evolves and you start having sequence of business operations that must be wrapped in a transaction, things get messy where you have to implement your own rollback compensation mechanism.
Hi,
I am aware of microservice and develop couple of ms for new platform but now i am suppose to create microservice out of big monolithic application. Our ms is in aws & need to use dynamodb whereas our monolithic application was using mysql. my question is if i take out one feature as ms and push that data into dynamdo but same data is require in our monolithic application, how to handle this? how to make this data available to monolithic service
Hi Arun,
First of all thanks for providing such a great blog post to gets started with Microservices. When we separate the part of applications into small or micro services then how one service call another service (all services are deployed on separate servers)? Is there any utility, tool or protocol to connect service within service.
We would be glad if you can give your talk on Indiamentor.com platform.
Thank you,
Dr. Rahul Bansal
You might want to check that ‘very similar’ article:
https://www.linkedin.com/pulse/monolithic-microservices-refactoring-java-ee-hamed-hatami
Hi Arun,
Thanks for your post. It is great and easy to understand for a newbie. I am looking at your example of Shopping cart application – conversion from monolith to microservices architecture. I have a few questions:
1) In case of microservices architecture, I understand that we should have separate containers for each microservice (order, catalog, web & user). Now should we be deploying tomcat server on each of these containers. I predict the answer is yes but then is it ok to have multiple instances of the tomcat running where as monolith needed only one.
2) How will the communication happen between the different containers?
Thanks in advance,
Olinka
A monolithic kernel has all OS services running within the privileged mode of processor.It has only one address space i.e. kernel space…
assignment writing service
They all require a database, multiple input forms, a simple workflow, and you can get plenty of examples of what looks nice around the web. assignment writing service
A high-standard post with all imperative information about Assignment Help UK services. Looking forward to avail the premium services.
I just want to thank you to share your information on your blog; this is a simple and useful.