Migrate from Spring to Quarkus

This guide will cover the migration process of a Spring application to a Quarkus application. There are already articles about migrating from Spring to Quarkus (e.g. https://developers.redhat.com/blog/2020/04/10/migrating-a-spring-boot-microservices-application-to-quarkus, https://dzone.com/articles/migrating-a-spring-boot-application-to-quarkus-cha). This guide will focus more on the devon4j specific aspects. We assume that a working Spring application exists, built in the classic devon4j specific way (e.g. Jump The Queue or My Thai Star).

Create the Quarkus application

We start with an empty Quarkus project. You can create the project with Maven on the command line or use the online generator. The advantage of the online generator is that you have a pre-selection of dependencies to use in your project. For starters, let’s select the basic dependencies required to develop a REST service with database connectivity (you can use one of the links in the Quarkus template guide): RESTEasy JAX-RS, RESTEasy Jackson, Hibernate ORM, Spring Data JPA API, JDBC Driver (choose the type of database you need), Flyway (if you have database migration schemas), SmallRye Health (optional for Health Monitoring)

The list does not include all required dependencies. We will add more dependencies to the project later. For now, generate the application with these dependencies.

Migration Toolkit from Red Hat

Red Hat provides a migration toolkit (MTA, Migration Toolkit for Applications), that supports migration of a Spring to a Quarkus application. There are several versions of this toolkit (e.g., a web console, a Maven plugin, or an IDE plugin). The MTA analyzes your existing application and generates a report with hints and instructions for migrating from Spring to Quarkus. For example, it gives you an indication of which dependencies are not supported in your project for a Quarkus application and which dependencies you need to swap them with. The analysis is rule-based, and you can also add your own rules that will be checked during analysis.

Entities

There is nothing special to consider when creating the entities. In most cases, you can simply take the code from your Spring application and use it for your Quarkus application. Usually, the entities extend a superclass ApplicationPersistenceEntity containing, for example, the id property. You can also take this class from your Spring application and reuse it.

Transfer objects

The next step is to create the appropriate transfer objects for the entities. In a devon4j Spring application, we would use CobiGen to create these classes. Since CobiGen is not usable for this purpose in Quarkus applications yet, we have to create the classes manually.

First, we create some abstract base classes for the search criteria and DTO classes. Normally, these would also be created by CobiGen.

Listing 24. AbstractSearchCriteriaTo
public abstract class AbstractSearchCriteriaTo extends AbstractTo {

  private static final long serialVersionUID = 1L;

  private Pageable pageable;

  //getter + setter for pageable
}
Listing 25. AbstractDto
public abstract class AbstractDto extends AbstractTo {

  private static final long serialVersionUID = 1L;

  private Long id;

  private int modificationCounter;

  public AbstractDto() {

    super();
  }

  //getter + setter

  @Override
  protected void toString(StringBuilder buffer) {
    ...
  }
}

The class AbstractTo, extended by other classes, would be provided by the devon4j-basic module in a devon4j Spring application. You can take the code from here and reuse it in your Quarkus project.

Now you can create your transfer objects. Most of the code of the transfer objects of your Spring application should be reusable. For Quarkus, we recommend (as mentioned here) to use *Dto instead of *Eto classes. Be sure to change the names of the classes accordingly.

Data Access Layer

In devon4j, we propose to use Spring Data JPA to build the data access layer using repositories and Querydsl to build dynamic queries. We will also use this approach for Quarkus applications, but we need to change the implementation because the devon4j modules are based on reflection, which is not suitable for Quarkus. In Quarkus we will use Querydsl using code generation. So for this layer, more changes are required and we can’t just take the existing code.

First, create a repository interface for your entity class that extends JpaRepository (see here).

To add QueryDSL support to your project, add the following dependencies to your pom.xml file:

Listing 26. pom.xml
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-jpa</artifactId>
  <version>4.3.1</version>
</dependency>
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
  <scope>provided</scope>
  <version>4.3.1</version>
</dependency>

As mentioned above, we will use QueryDSL with code generation. For this, add the QueryDSL annotation processor to your plugins:

Listing 27. pom.xml
<plugins>
...
  <plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
      <execution>
        <phase>generate-sources</phase>
        <goals>
          <goal>process</goal>
        </goals>
        <configuration>
          <outputDirectory>target/generated-sources/annotations</outputDirectory>
          <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
        </configuration>
      </execution>
    </executions>
  </plugin>
</plugins>

To implement the queries, follow the corresponding guide.

Set the following properties in the application.properties file to configure the connection to your database (see also here):

quarkus.datasource.db-kind=...
quarkus.datasource.jdbc.url=...
quarkus.datasource.username=...
quarkus.datasource.password=...

Logic Layer

For the logic layer, devon4j uses a use-case approach. You can reuse the use case interfaces from the api module of the Spring application. Again, make sure to rename the transfer objects.

Create the appropriate class that implements the interface. Follow the implementation section of the use-case guide to implement the methods. For mapping the entities to the corresponding transfer objects, see the next section.

Mapping

For bean mapping, we need to use a completely different approach in the Quarkus application than in the Spring application. For Quarkus, we use MapStruct, which creates the mapper at build time rather than at runtime using reflection. Add the following dependencies to your pom.xml.

Listing 28. pom.xml
<dependency>
  <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct-processor</artifactId>
  <version>1.4.2.Final</version>
</dependency>
<dependency>
  <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct</artifactId>
  <version>1.4.2.Final</version>
</dependency>

Then you can create the mapper as follows:

Listing 29. Mapper
@Mapper(componentModel = "cdi")
public interface YourEntityMapper {
  YourEntityDto map(YourEntity entity);

  YourEntity map(YourEntityDto dto);

  ...
}

Inject the mapper into your use-case implementation and simply use the methods. The method implementations of the mapper are created when the application is built.

Service Layer

For the implementation of the service layer, we use the JAX-RS for both Quarkus and Spring applications to create the REST services. Classic devon4j Spring applications rely on Apache CFX as the implemention of JAX-RS. For Quarkus, we use RESTEasy. Since both are implementations of JAX-RS, much of the Spring application code can be reused.

Take the definition of the REST endpoints from the api module of the Spring application (make sure to rename the transfer objects), inject the use-cases from the logic layer and use them in the REST service methods as follows:

Listing 30. REST service
@Path("/path/v1")
public class YourComponentRestService {

  @Inject
  UcFindYourEntity ucFindYourEntity;

  @Inject
  UcManageYourEntity ucManageYourEntity;

  @GET
  @Path("/yourEntity/{id}/")
  public YourEntityDto getYourEntity(@PathParam("id") long id);

    return this.ucFindYourEntity.findYourEntity(id);
  }

  ...
}

Summary

As you have seen, some parts hardly differ when migrating a Spring application to a Quarkus application, while other parts differ more. The above sections describe the parts needed for simple applications that provide REST services with a data access layer. If you add more functionality, more customization and other frameworks, dependencies may be required. If that is the case, take a look at the corresponding guide on the topic in the devon4j documentation or check if there is a tutorial on the official Quarkus website.

Furthermore, we can summarize that migrating from a Spring application to a Quarkus representative is not complex. Although Quarkus is a very young framework (release 1.0 was in 2019), it brings a lot of proven standards and libraries that you can integrate into your application. This makes it easy to migrate and reuse code from existing (Spring) applications. Also, Quarkus comes with Spring API compatibility for many Spring modules (Spring Data JPA, Spring DI, etc.), which makes it easier for developers to reuse their knowledge.

Last updated 2023-11-20 10:37:01 UTC