Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add User Story #1882

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion _data/authors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -513,4 +513,11 @@ yrodiere:
emailhash: "2a8bdd4ffd282b7185c74b52ab452617"
job_title: "Principal Software Engineer"
twitter: "yoannrodiere"
bio: "Lead developer on Hibernate Search (http://hibernate.org/search/), and one of the main contributors to the Hibernate extensions (ORM, Search, Validator) of Quarkus."
bio: "Lead developer on Hibernate Search (http://hibernate.org/search/), and one of the main contributors to the Hibernate extensions (ORM, Search, Validator) of Quarkus."
dodalovicgran:
name: "Dusan Odalovic"
email: "[email protected]"
emailhash: "9dd5fcd9a02ef03f6615f10c5f159a18"
job_title: "Software Engineer"
twitter: "OdalovicDusan"
bio: "Software Engineer at GRAN Software Solutions GmbH"
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
---
layout: post
title: "Quarkus as SaaS Backend: Time Tracking App Case Study"
date: 2024-01-29
tags: user-story
author: dodalovicgran
---
:imagesdir: /assets/images/posts/quarkus-user-stories/gran

= Revolutionizing Time Tracking: How Quarkus Transformed Our Backend Development

[.customer-logo]
image::gran-logo.png[GRAN logo,200]

[.customer-quote]

GRAN Software Solutions is a German company that designs and builds modern back-end solutions.
We work with large automotive clients and others to restructure and create new solutions.
We also develop and offer SaaS tools to help us and others in our daily work.

One such tool we built for ourselves and others is a time-tracking application called https://sheetty.com[Sheetty].

== The Time Tracking Challenge

image::frustrated-developer.png[alt="Frustrated Developer",align="center",width=640]

We needed to create a time-tracking application because the existing solutions on the market did not meet our specific requirements.
They were either not designed for developers lacked the simplicity we needed, or were loaded with unnecessary features.
We wanted to build a tool that was perfectly tailored to our needs, using the extensive experience we had gained from working on client projects over the years.
We also wanted to create a more modern and user-friendly design, which would be fun to use and incorporate newer technologies such as Quarkus.

The main issue we faced with existing time-tracking solutions was the lack of an easy way to switch between clients.
We also found that they did not support quick actions or shortcuts, which we were used to, and there was no visual way to see the time entries we made during the day.
Additionally, we wanted to track time within the context of contracts signed with our clients in terms of daily rates and contract caps.
That's why we decided to create a custom solution to address all of these specific needs.

== Discovering Quarkus

When we were choosing the technology stack to use for our backend, our main goal was to use technologies that we were already familiar with, such as the Kotlin programming language, Spring Boot framework, and Postgres database.
We also wanted to select an ecosystem that could provide us with libraries for database connectivity, web client, caching, and other similar features.
Additionally, we wanted to use a high-performance solution to keep our hosting costs low and avoid high memory requirements.

After analyzing various solutions on the market, we decided to use the Quarkus framework as it met all of our requirements.

== Our Backend Development Experience with Quarkus: The Key Features

We have designed our application architecture to separate the front-end and back-end parts.
To secure our back-end APIs in a modern and secure way, we opted to use JSON web tokens, and Quarkus has excellent support for them.
We also use role-based security for our APIs, and Quarkus makes it easy for us to implement this.
We have different roles in our application, such as regular users and admins, and this information is encoded in our JSON web tokens.
Quarkus ensures that these tokens are not tampered with or manipulated when they reach our back-end systems.

.`@RolesAllowed` for authorization of our API endpoints
[source,kotlin]
----
@Path("/clients")
@RolesAllowed("User")
@Produces(MediaType.APPLICATION_JSON)
@ApplicationScoped
class ClientResource(
private val getClientsHandler: GetClientsHandler,
private val newClientHandler: NewClientHandler,
----

We relied heavily on rich JSON support to model our data flexibly and delegate much of the functionality to Postgres itself to manipulate the data.
This way, we could pass the already-built JSON objects back to the API client, which significantly reduced the time it took to make design decisions in the application code.
Quarkus provided fantastic support for JSON object APIs.
We believe that Postgres is the right place to perform data manipulations and aggregations, not the application code, due to performance and code maintenance reasons.

.Using `JsonObject` to pass our data in and out
[source,kotlin]
----
@GET
@Produces(MediaType.APPLICATION_JSON)
suspend fun getProfile() = db.preparedQuery(
"""select profile from "user" where email = $1""".trimIndent()
).execute().awaitSuspending().first().getJsonObject("profile")
----

Although Quarkus primarily targets Java programming language, Kotlin support is also quite good.
We used coroutines and suspending functions, which allowed for greater performance and much simpler code compared to some other asynchronous programming models that are available.
Kotlin's structured concurrency enabled us to write seemingly sequential code but in reality, very performant asynchronous code.
Quarkus provides excellent Kotlin extension methods built on top of existing asynchronous APIs such as mutiny.

We executed the database migration on application startup, which was very important for us.
Fortunately, Quarkus has excellent Flyway support, so all our database migrations were in one place and executed during our back-end booting process.
This kept our database schema and data transparent and reproducible.

.Using Flyway to execute database migrations
image::db-migrations.png[alt="Database Migrations",align="center",width=640]

For our deployments, we use Kubernetes.
Before using Quarkus, we described our application requirements using helm packaging, but with Quarkus, we opted for another approach as Quarkus offers a great Kubernetes extension.
Instead of writing any code, we described our Kubernetes resources using an application.yaml file, keeping our complete application configuration in one place.
This extension generated Kubernetes resource files behind the scenes, which we then applied to our Kubernetes cluster.
This works well for us.

.Using the Kubernetes extension to generate Kubernetes resources
image::k8s-config.png[alt="Kubernetes configuration",align="center",width=640]

For packaging our back-end API, we used the Jib extension.
To package our application in a container, all we had to do was use the application.yaml file and set all the required parameters such as image name tags repository, and so on.
We didn't have to maintain the Docker file on our own, which was very convenient.

Our time-tracking application needs to send emails to our users and admins on various occasions.
To keep things simpler, we decided not to go for any third-party API-driven email-sending approach.
Instead, we send emails ourselves, and for that purpose, we use Qute email templates, which make composing and sending emails to our users very simple.
This extension provides support for coding coroutines, allowing for non-blocking sending and higher throughput.

.Using Qute email templates to send emails
image::qute-templates.png[alt="Qute Templates",align="center",width=640]

== Development Journey

The Quarkus development process has been excellent so far.
Compared to other frameworks like Spring Boot, Quarkus has a faster startup time and a smaller memory footprint.
It also provides profiles, which allows us to have slightly different configurations or behaviors between environments.
We can easily substitute some hard-to-run third-party services with local mocks, leaving the application code unchanged.
Quarkus is also great in terms of configuration and how easily we can overwrite values stored in the application.yaml file with external environment variables.
Although the hot reload mode didn't work well with Kotlin, I believe all the bugs related to it will be solved in upcoming releases.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got links to some of those bugs you encountered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During development, we had to restart our running service most of the time for code changes to take effect.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which version of Quarkus were you targetging?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3.6.1


Our backend API functionalities took approximately a month and a half to complete.
Considering that only two developers worked on the backend, I think it was a good result.
In this phase of our product lifecycle, we decided against writing automated tests due to constantly revisiting requirements and our needs.
Instead, we went for manual testing for now.
Once our Time tracking application gets more active users, we plan to start writing automated tests using Quarkus test support, including test containers and others.
Developing a full-blown API, including API security with JSON web tokens and authorization in place, having database migration automatically applied during application boot time, having a flexible and maintainable code base revolving around JSON, with the ability to package and deploy our API to our Kubernetes cluster, is quite an achievement for just a month and a half of work.

== Conclusion

We are glad to share that using Quarkus, Kotlin, and Postgres as the foundation of our backend API has been a fun and productive experience for us.
Quarkus's ability to experiment quickly and leverage ready-made components has made us confident that we made the right technological choice.
Although there are some imperfections with hot reload and some quirks with Kotlin, we are waiting for the fixes to be made and have no doubt that Quarkus is the best solution for us.

We are working smart and hard to bring new features to our time-tracking application.
To achieve this, we will continue to use the great features provided by Quarkus, which dramatically reduce the time needed to roll out our features quickly.
We invite you to try our time tracker at https://sheetty.com[sheetty.com].


Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading