Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Modernizing Pedal: Breaking down a Javå monolith into Quarkus microservices

Understand how to breakdown a monolith into Quarkus based microservices

May 2, 2024
Yashwanth Maheshwaram Ian Lawson

Share:

    In the mid-2010s, Netflix and Google detailed how they saved costs while scaling their software infrastructure’s components to meet rising demands. Since then, most engineers have embraced this microservices architecture. Microservices help divide a single monolithic application into small units, each acting as a service to deliver specific functions.

    For companies transitioning from on-premise software to a hybrid cloud infrastructure, refactoring the application from a monolith into microservices offers several advantages. The microservice architecture isolates services, allowing companies to loosely coupled software components. This flexibility enables deploying and running specific services on premise or in the cloud as part of a hybrid setup. Consequently, companies can keep personal information on premise to comply with regulations while simultaneously scaling customer-facing services in the cloud to meet demand.

    This tutorial will demonstrate how to break down Pedal, , a monolithic e-commerce applicationinto microservices. The Java application manages bicycle sales and includes features for employees to create an account and post bikes for sale for customers to make purchases. In general, the following are the steps that should fit most applications:

    • Identify Microservices
    • Refactor the application
    • Deploy microservices
    • Use Red Hat OpenShift Virtualization for the services/apps that cannot be broken down right away

    Prerequisites

    To follow along, you’ll need to have:

    • Basic understanding of the Java™ programming language 
    • Java 17 installed to be able to run Java applications
    • Quarkus CLI installed
    • Visual Studio Code with the Language Support for Java™ by Red Hat and the Quarkus extensions. You can use another code editor, but it’s recommended to use Visual Studio Code for Quarkus. As such, we use Visual Studio Code in this demonstration. 
    • Podman Desktop installed and running to help Quarkus  to build your application automatically as you build and save your changes

    For installing the required prerequisites based on your operating system, you can refer to this page.

    Identify microservices

    The first step to refactoring your monolith into microservices is identifying the application’s components to isolate into independent services. Following the single-responsibility microservice design principle, each service should handle a specific feature or function.

    The Pedal application’s technology stack comprises Java Code, SQL Server, Postgres, and Thymeleaf. The application contains controllers and modules for employees to authenticate and create or update the bikes for sale. 

    Pedal’s microservice architecture requires at least the following services:

    • A User service to handle customer and employee registration
    • An Auth service to handle employee and customer authentication
    • A Bike service to handle create, read, update, and delete (CRUD) operations related to bike data
    • An Order service to handle bike orders
    • A Payment service to process customer payments through a third-party payment processor

    The following section demonstrates how to create two of these services: Bike and User. You’ll use similar techniques to make the rest.

    Refactor the application

    As we refactor the Pedal app into small services, we’ve chosen the following technologies. Let’s understand a bit more about the choices

    • Quarkus to generate the boilerplate Java projects for this demonstration. Quarkus offers great developers joy with live reload, quick startup times, low memory footprint. It is also container first, unifies Imperative and Reactive programming paradigms. With support for the latest standards and best of the breed libraries, Quarkus is very well capable of building and managing Pedal’s requirements.
    • Red Hat Service Interconnect to connect the services, we use Red Hat Service Interconnect. It allows disparate services to communicate as one regardless of their environment.  

    First, execute the Git command below to clone the monolith Pedal application from its GitHub repository. You can manually download it from the repository if you don’t have the Git command-line interface (CLI):

    git clone https://github.com/redhat-developer-demos/pedal-traditional

    Then, launch Visual Studio Code. You’ll build the microservices using the Pedal app’s existing code.

    Creating the User service

    • Generate the application
    • Prepare your environment
    • Configuring the database

    Open the Quarkus Application Generator to generate a Java project. Change the Artifact to user-service. Keep the other default settings and add the following new dependencies:

    • RESTEasy Reactive: Used to create highly performant, non-blocking REST endpoints in microservices, which is essential for handling numerous simultaneous requests efficiently.
    • RESTEasy Reactive JSON-B: This dependency is used for binding JSON data to Java objects (JSON-B) in a reactive way, allowing efficient JSON data processing in RESTEasy Reactive services.
    • RESTEasy Reactive Jackson: It provides JSON processing capabilities using the Jackson library, offering an alternative to JSON-B for developers familiar with Jackson's data-binding features in RESTEasy Reactive services.
    • RESTEasy Reactive REST Client: This is used for creating reactive web clients within microservices, enabling non-blocking, asynchronous communication with other services, which is crucial for microservices architecture.
    • REST Client Reactive JSON-B: It integrates JSON-B with REST Client Reactive, allowing developers to perform JSON serialization and deserialization in a reactive manner for client-service communication.
    • REST Client Reactive Jackson: Similar to REST Client Reactive JSON-B, this dependency uses Jackson for JSON processing in a reactive web client, catering to preferences or existing standards in microservice communication.
    • Container Image Jib: Jib is used for building optimized Docker and OCI container images for Java applications, including microservices, without needing a Dockerfile, thereby streamlining the containerization process.
    • Hibernate ORM: Provides a framework for mapping an object-oriented domain model to a relational database, which is key in microservices that need to interact with databases while maintaining a domain-driven design.
    • JDBC Driver-PostgreSQL: This driver enables Java applications to connect to a PostgreSQL database, necessary for microservices that require data persistence or retrieval from a PostgreSQL database.
    • Hibernate ORM With Panache: This simplifies Hibernate ORM usage, reducing boilerplate code and making it easier to write database operations, thereby accelerating development in microservices interacting with databases.
    • SmallRye OpenAPI: Used for generating OpenAPI specifications for microservices, enabling clear documentation and client generation, which is critical for service discovery and integration in a microservices architecture.

    Click Generate your application to be able to download the application.

    Quarkus application interface. The "Configure Your Application" page is open. On the right is a blue button that says, "Generate your application." It's outlined with a red box.

    In your preferred directory, create a parent folder called Microservices. Copy the unzipped user-service application in the Microservices folder and open the new project to your code editor. 

    Before we jump into running the application, ensure that you have Podman running. This is needed to let Quarkus build the application images. You can download and install Podman Desktop from https://podman-desktop.io/. 

    To test if the application works based on the generated boiler template, run the following command in the root of your project: 

    quarkus dev

     

    Navigate to http://localhost:8080/hello. A message reading “Hello from RESTEasy Reactive” should be displayed in the browser. 

    Setting up the Database

    Open the src/main/resources/ application.properties file within the new Java project to specify the Java Persistence API (JPA) data source and the other for loading Swagger:

    # DATABASE CONFIGURATION
    quarkus.datasource.db-kind=postgresql
    quarkus.hibernate-orm.database.generation=drop-and-create
    
    # SWAGGER
    quarkus.smallrye-openapi.path=/openapi
    

    These configurations set up the Java application to use PostgreSQL as the database. The database strategy is set to drop and create, which means the database will be dropped and recreated upon every restart. Use this command to list running containers:

    podman ps

    You should see a postgres container running the database and testcontainers/ryuk container which orchestrates the services. 

    Screenshot showing a Postgres container running the database and a testcontainers/ryuk container running.

    Next, create a User.java file within the /src/main/java/org/acme/User/Service package using the following code from the Pedal application:

    package org.acme.user.service;
    # TODO: Refer to imports in code from repository
    @Entity
    @Table(name = "service_user")
    public class User extends PanacheEntity {
    @Column(name = "fullname")
    private String fullname;
    @Column(name = "username")
    private String username;
    @Column(name = "email")
    private String email;
    @Column(name = "date_created")
    private Date dateCreated;
    @Column(name = "password")
    private String password;
    @Column(name = "user_role")
    private String userRole;

    The code block above contains the JPA entity for a user within the service. Do not provide the @Id, getters, or setters since Panache takes care of that. 

    Now, create a UserRepository.java file within the /src/main/java/org/acme/User/Service package to create a repository for retrieving users based on their username with your User entity. Add the code below to the UserRepository.java file:

    package org.acme.user.service;
    
    # TODO: Refer to imports in code from repository
    
    @ApplicationScoped
    public class UserRepository implements PanacheRepository<User> {
    
    public Optional<User> findUserByUsername(String username) {
            return find("username", username).singleResultOptional();
    }
    
    public List<User> getAllUsers() {
            return listAll();
    }
    
    public Optional<User> getUserById(Long id) {
            return findByIdOptional(id);
    }
    
    @Transactional
    public User createUser(User user) {
         persist(user);
         return user;
    }
    }
    

    Create a UserService.java file with the following code to contain the application’s entity mapping:

    package org.acme.user.service;
    
    # TODO: Refer to imports in code from repository
    
    @ApplicationScoped
    public class UserService {
    
    	@Inject
    	UserRepository userRepository;
    
    	public List<User> getAllUsers() {
         	return userRepository.getAllUsers();
    	}
    
    	public Optional<User> getUserById(Long id) {
         	return userRepository.getUserById(id);
    	}
    
    	public User createUser(User user) {
         	return userRepository.createUser(user);
    	}
    }

    Next, create a UserResource.java file within the /src/main/java/org/acme/User/Service package to define the API routes within the User service. 

    Add the following code to the UserResource.java file to define three endpoints for creating, retrieving, and fetching a specific user through the microservice:

    package org.acme.user.service;
    
    # TODO: Refer to imports in code from repository
    
    @Path("/api/users")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public class UserResource {
    
    	@Inject
    	UserService userService;
    
    	@GET
    	public List<User> getAllUsers() {
         	return userService.getAllUsers();
    	}
    
    	@GET
    	@Path("/{id}")
    	public User getUserById(@PathParam("id") Long id) {
         	java.util.Optional<User> user = userService.getUserById(id);
         	if (user.isPresent()) {
             	return user.get();
         	} else {
             	throw new NotFoundException("User with ID " + id + " not found");
         	}
    	}
    
    	@POST
    	public User createUser(User user) {
         	return userService.createUser(user);
    	}
    }
    

    In the repository, there’s an import.sql file with User data inserts. To initialize this data for use in your application, add the command below to the application.properties file:

    quarkus.hibernate-orm.sql-load-script=import.sql

    The command specifies the location of the SQL file to be executed when JPA initializes the database. 

    Finally, navigate to http://localhost:8080/q/swagger-ui/ to try your User service endpoints:

    LocalHost open showing the Swagger UI. The page verifies the user-service API is functional.

    Creating the pedal-bike-service

    Open Quarkus to generate another Java project named bike-service. Ensure the project includes the same dependencies as the User service:

    Screenshot of the "Configure your Application Page" in the Quarkus application.

    Unzip the bike-service application and put it alongside user-service in the same Microservices folder you created earlier. Test the source code boiler template like you did with the User service. 

    Next, open the application.properties file to specify the service’s data source and Swagger configurations below:

    # DATABASE CONFIGURATION
    quarkus.datasource.db-kind=postgresql
    quarkus.hibernate-orm.database.generation=drop-and-create
    
    # SWAGGER
    quarkus.smallrye-openapi.path=/openapi
    
    Using the following code, create a Bike.java file within the /src/main/java/org/acme/Bike/Service package to contain the Bike entity within the Bike service:
    
    package org.acme.bike.service;
    
    # TODO: Refer to imports in code from repository
    
    @Entity
    @Table(name = "bikes")
    public class Bike extends PanacheEntity {
    
    	@Column(name = "name")
    	private String name;
    
    	@Column(name = "model")
    	private String model;
    
    	@Column(name = "date_created")
    	private Date dateCreated;
    
    	@Column(name = "price")
    	private Integer price;
    
    	@Column(name = "image")
    	private byte[] image;
    
    	@Column(name = "warranty_status")
    	private String warrantyStatus;
    	
    }

     

    Now, create a BikeRepository.java file within the /src/main/java/org/acme/Bike/Service package to create a repository using the Bike entity. Add the following code to the file:

    package org.acme.bike.service;
    
    # TODO: Refer to imports in code from repository
    
    @ApplicationScoped
    public class BikeRepository implements PanacheRepository<Bike> {
    
    	@Transactional
    	public Bike postBikeAd(Bike bikeData) {
      	persist(bikeData);
    	return bikeData;
    	}
    	}
    
    	Then, create a BikeService.java file to store the following entity-related statements:
    
    	package org.acme.Bike.Service;
    
    	# TODO: Refer to imports in code from repository
    
    	@ApplicationScoped
    	public class BikeService {
    	@Inject
    	BikeRepository bikeRepository;
    
    	@Transactional
    	public List<Bike> retrieveBikes() {
         	return bikeRepository.listAll();
    	}
    
    	public Bike postBikeAd(Bike bike) {
         	return bikeRepository.postBikeAd(bike);
    	}
    }
    

    Create a BikeResource.java file within the /src/main/java/org/acme/Bike/Service package to define the API routes within the Bike microservice.

     

    package org.acme.bike.service;
    
    # TODO: Refer to imports in code from repository
    
    @Path("/api/bikes")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public class BikeResource {
    
    	@Inject
    	BikeService bikeService;
    
    	@GET
    	public Response getAllBikes() {
         	List<Bike> bikes = bikeService.retrieveBikes();
         	return Response.ok(bikes).build();
    	}
    
    	@POST
    	public Response postBike(Bike bike) {
         	Bike newBike = bikeService.postBikeAd(bike);
         	return Response.ok(newBike).build();
    	}
    }

     

    NOTE: As with the User service, ensure you have this property in the application.properties:

    quarkus.hibernate-orm.sql-load-script=import.sql

    Finally, navigate to http://localhost:8080/q/swagger-ui/ to try your Bike service endpoints:

    LocalHost open showing the Swagger UI. The page verifies the bike-service API is functional.

    Serving the UI

    While the above microservices handle the important Bike and User-related functions of the application, you also need a service that delivers the web pages to the user so they can navigate the site. To do so, you’ll create a third Quarkus service designed to feed pages back to requests. You can re-use the pages from our original legacy application, and create a service to handle presenting them. 

    Create the Quarkus project through the same steps outlined above. Once you have the project set up, follow these instructions to serve static files and pages for the Pedal application:

    • Place static files: Put your static files, such as HTML, CSS, and JavaScript, in the src/main/resources/META-INF/resources directory of your Quarkus application. For instance, if you have a file like src/main/resources/META-INF/resources/index.html, it will be accessible at http://localhost:8080/index.html. 

    For Pedal, you’ll want to move the following HTML files, as well as the static, files from the folders, to this location. 

    Screenshot of different files (auth, bike, dashboard, order, and static), followed by a list of different files (create-bike.html, customer-support.html, dashboard.html. home.html, index.html, login.html, orders.html, profile.html, purchase.html, signup.html, update-bike.html, and users.html.

    • Set index page: In your application.properties file, specify the quarkus.http.static-resources.index-page property to be the path of the page you want to serve at the root URL. For the Pedal application, you’ll want something like quarkus.http.static-resources.index-page=pedal/index.html.
    • Enable static asset caching: To improve performance, you can enable static asset caching by adding quarkus.http.static-resources.caching-enabled=true to your application.properties file. You can also configure the cache’s lifespan and size with quarkus.http.static-resources.max-age and quarkus.http.static-resources.max-cache-size, respectively.
    • Set up SSL/TLS: For secure communication, set up SSL/TLS by adding the following properties to your application.properties file:

      quarkus.http.ssl.certificate.files=/path/to/certificate
      quarkus.http.ssl.certificate.key-files=/path/to/key

    This allows your static resources to be accessed securely via https://localhost:8080.

    • Serve static libraries with WebJars: To manage and serve static libraries like jQuery and Bootstrap, use WebJars. Add the following dependencies to your pom.xml file for jQuery:
    <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.1.1</version>
    </dependency>
    <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-webjars-locator</artifactId>
    </dependency>

    After restarting quarkus dev, you should be able to access the jQuery library at http://localhost:8080/webjars/jquery/jquery.min.js.

    To access your webpage, navigate to http://localhost:8080/index.htm. You’ll be greeted with a welcome screen, as shown below:

    Screenshot of the Pedal app landing page. It opens with a heading that reads, "Reliable Bikes You Can Trust" and lists available bikes.

     

    By following these steps, you can set up a Quarkus service to serve the UI for the Pedal application, providing users with a seamless web experience.

    Rehosting made easy with OpenShift Virtualization

    The Red Hat OpenShift Virtualization helps organizations jumpstart the transition to modernizing operations. They get to use most of their investments in virtualization to use containers while on the same platform. This unified process increases operational efficiency by optimizing most idle physical hardware, which allows you to run multiple workloads within a single machine, resulting in cost savings.

    In an upcoming article, you’ll learn how to use OpenShift Virtualization to create workloads within virtual machines for the Pedal application services developed here.

    Next steps

    Microservices allow software engineers to partition large software systems into small, isolated, independent services. Engineers can then independently scale and manage these services in hybrid cloud systems.

    For example, consider Pedal’s owner, who may prioritize enhancing the security of the User service to protect customer information while keeping the Bike service openly accessible for anyone to browse bike listings. Pedal engineers may need to scale the User service to accommodate growth or the Bike service to hold all the new holiday inventory. Thanks to the microservice architecture, they can independently take specific actions with each service. 

    You’ve now learned how to convert a monolith Java application into microservices using Eureka. You can apply the same technique to benefit from virtualizing your own application’s microservices.

    Your next step is to deploy the Pedal application’s microservices to an OpenShift cluster using Red Hat Virtualization. Visit the Red Hat Developer portal to learn more about building a hybrid cloud environment for your microservices.

    Recent Posts

    • AI meets containers: My first step into Podman AI Lab

    • Live migrating VMs with OpenShift Virtualization

    • Storage considerations for OpenShift Virtualization

    • Upgrade from OpenShift Service Mesh 2.6 to 3.0 with Kiali

    • EE Builder with Ansible Automation Platform on OpenShift

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue