DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. Databases
  4. Think Reactive and Native With Quarkus, Kotlin, and GraphQL

Think Reactive and Native With Quarkus, Kotlin, and GraphQL

Native applications are an essential factor in containerization. Reactive programming is an excellent choice for developing stream-based or queue-based applications

Ashok Gudise user avatar by
Ashok Gudise
·
Mar. 03, 23 · Tutorial
Like (4)
Save
Tweet
Share
4.60K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

In today’s world of software development, the terms “native” and “reactive” have gained significant popularity, becoming crucial considerations for developers, architects, and businesses alike.

Regardless of whether you’re building front-end applications or back-end systems, the native and reactive approach has become a crucial component of modern software development’s non-functional requirements. This blog aims to explore the significance of these two concepts and why they have become so essential.

We will also take a closer look at where these two concepts will fit in the three main categories of prevalent software solutions in the IT industry today: Request-Response (Synchronous), queue-based (Asynchronous), and event-based (Streams/Event Sourcing). Finally, we will examine how native and reactive architectures can bring significant benefits to each of these categories.

About “Native”

Containerization has become a popular approach to deploying applications in modern cloud environments. With containers, applications are packaged into a single container image, which can then be deployed across different environments consistently. However, when it comes to running applications in containers, there are two main options: running as a traditional virtual machine (VM) or running as a native application. Native applications are built specifically for the target platform, resulting in better performance, faster startup times, lower memory consumption, and better runtime performance. They can be easily scaled horizontally, increasing resource utilization efficiency. Additionally, running applications natively reduces the attack surface area, making the application more secure. In summary, native applications are an essential factor in containerization, providing better performance, scalability, resource efficiency, and security, and are crucial for the success of modern cloud environments. You can learn more about Cloud-Native here.

About “Reactive”

Reactive programming has gained popularity in recent years, and for good reason. One significant benefit of reactive programming is its ability to handle stream-based or queue-based applications. In such applications, the processing of incoming messages or events is time-sensitive and requires immediate action. Reactive programming provides a solution for these scenarios by allowing the developer to write applications that can react immediately to incoming events rather than waiting for a response from the previous event. This means that the application can handle high volumes of incoming data without blocking, resulting in a faster and more efficient application. Additionally, reactive programming allows for better scalability and resilience, as it can handle large amounts of incoming data without overloading the system. In summary, reactive programming is an excellent choice for developing stream-based or queue-based applications, as it allows for immediate response and efficient handling of high volumes of data, resulting in a more scalable, resilient, and performant application.

Tech Stack

Quarkus is an emerging alternative to the Spring framework from RedHat. It is rapidly gaining popularity due to its high performance and efficient resource usage. One of the key reasons for its popularity is its ability to use a compile-time approach to build native apps, resulting in lightweight and fast apps. This approach, coupled with Quarkus’ support for GraalVM, allows Quarkus apps to consume minimal resources, making them ideal for running in resource-constrained environments like containers or serverless environments.

Quarkus also provides a streamlined development experience for developers, with extensive development tools and hot reloading support. Quarkus is designed to work well with cloud-native architectures and provides out-of-the-box support for several cloud-native technologies like Kubernetes, Istio, and Knative.

Quarkus Reactive is a powerful framework for building reactive applications, with features like Panache that make it easy to work with databases like MySQL using plain old Java objects (POJOs). With Panache, developers can create entities that map to database tables and perform CRUD operations using simple, type-safe methods.

In summary, Quarkus is an excellent alternative to the Spring framework, providing several advantages such as high performance, efficient resource usage, cloud-native support, and a streamlined development experience, making it an ideal choice for modern cloud-native architectures.

Kotlin: is a statically typed programming language that was designed to be more concise and expressive than Java while still being fully interoperable with Java code. Developed by JetBrains, Kotlin is open source and has rapidly gained popularity due to its ease of use, concise syntax, and improved developer productivity. Kotlin is used for developing a wide range of applications, from Android mobile apps to server-side applications and beyond. Its adoption has been driven by major companies like Google, Netflix, and Pinterest, making Kotlin a popular choice among developers for modern software development.

GraphQL: Developed by Facebook, GraphQL is a query language for APIs that has since been made available to the wider developer community. It offers a more efficient, powerful, and flexible option compared to REST for modern web APIs. With GraphQL, you can specify the exact data you need, reducing issues of over-fetching and under-fetching. Key features are Mutations and GraphQL-UI that come along with your API.

Demo Scene

Suppose that AWS Cloud does not offer its crucial “Reliability” pillar, and you are responsible for managing your app (EC2 instances/pods) independently. Your task is to create an API that is reactive, which means you need to receive real-time notifications when a pod is terminated or created. Let us develop the API to fulfill these essential requirements.

— Even if it’s just a hypothetical scenario, it can still be alarming to consider the potential challenges and risks that may arise in such a situation 

Database Schema

Define the main components of AWS Network like VPC, Region, Subnet, EC2, etc.

SQL
 
CREATE TABLE region (
                        region_id INT NOT NULL AUTO_INCREMENT,
                        region_name VARCHAR(255) NOT NULL,
                        PRIMARY KEY (region_id)
);

CREATE TABLE vpc (
                     vpc_id INT NOT NULL AUTO_INCREMENT,
                     vpc_name VARCHAR(255) NOT NULL,
                     region_id INT NOT NULL,
                     PRIMARY KEY (vpc_id),
                     FOREIGN KEY (region_id) REFERENCES region(region_id)
);

CREATE TABLE azone(
                      az_id INT NOT NULL AUTO_INCREMENT,
                      az_name VARCHAR(255) NOT NULL,
                      region_id INT NOT NULL,
                      PRIMARY KEY (az_id),
                      FOREIGN KEY (region_id) REFERENCES region(region_id)
);

CREATE TABLE subnet (
                        subnet_id INT NOT NULL AUTO_INCREMENT,
                        subnet_name VARCHAR(255) NOT NULL,
                        vpc_id INT NOT NULL,
                        az_id INT NOT NULL,
                        PRIMARY KEY (subnet_id),
                        FOREIGN KEY (vpc_id) REFERENCES vpc(vpc_id),
                        FOREIGN KEY (az_id) REFERENCES azone(az_id)
);

CREATE TABLE ec2 (
                     ec2_id INT NOT NULL AUTO_INCREMENT,
                     ec2_name VARCHAR(255) NOT NULL,
                     subnet_id INT NOT NULL,
                     PRIMARY KEY (ec2_id),
                     FOREIGN KEY (subnet_id) REFERENCES subnet(subnet_id)
);


Now, Let’s create a Quarkus Project by using IntelliJ Idea’s Project Initializer, or you can simply visit the official website and create it.

New Project

Project Initializer

Project Initializer

Modules

Modules


Define Your Entity Classes

Kotlin
 
@Entity
@Cacheable
@Table(schema = "sysopsdb", name="azone")
class Azone {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="az_id")
    var id: Long? = null

    @Column(name ="az_name")
    lateinit var name: String

    @ManyToOne(fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = "region_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    var region: Region? = null

    @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, mappedBy = "azone")
    var subnets: Set<Subnet> = HashSet<Subnet>()

}

@Entity
@Cacheable
@Table(schema = "sysopsdb", name="ec2")
class EC2{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="ec2_id")
    var id: Long? = null

    @Column(name ="ec2_name")
    lateinit var name: String

    @ManyToOne(fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = "subnet_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    var subnet: Subnet? = null
}

@Entity
@Cacheable
@Table(schema = "sysopsdb", name="region")
class Region {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="region_id")
    var id: Long? = null

    @Column(name ="region_name")
    lateinit var name: String

    @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, mappedBy = "region")
    val azs: Set<Azone> = HashSet<Azone>()
}

@Entity
@Cacheable
@Table(schema = "sysopsdb", name="subnet")
class Subnet {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="subnet_id")
    var id: Long? = null

    @Column(name ="subnet_name")
    lateinit var name: String

    @ManyToOne(fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = "vpc_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    val vpc: VPC? = null

    @ManyToOne(fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = "az_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    val azone: Azone? = null

    @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, mappedBy = "subnet")
    val ec2s: Set<EC2> = HashSet<EC2>()
}

@Entity
@Cacheable
@Table(schema ="sysopsdb", name = "vpc")
class VPC {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="vpc_id")
    var id: Long? = null

    @Column(name ="vpc_name")
    lateinit var name: String

}


Panache Repositories

Kotlin
 
@ApplicationScoped
class AzoneRepository: PanacheRepository<Azone> {
    fun findByName(name: String) = find("name", name).firstResult<Azone>()
    fun deleteInstance(id: Long) = delete("id", id)
}

@ApplicationScoped
class EC2Repository: PanacheRepository<EC2> {
    fun findByName(name: String) = find("name", name).firstResult<EC2>()
    fun deleteInstance(id: Long) = delete("id", id)
}

@ApplicationScoped
class RegionRepository: PanacheRepository<Region> {
    fun findByName(name: String) = find("name", name).firstResult<Region>()
    fun deleteInstance(id: Long) = delete("id", id)
}

@ApplicationScoped
class SubnetRepository: PanacheRepository<Subnet> {
    fun findByName(name: String) = find("name", name).firstResult<Subnet>()
    fun deleteInstance(id: Long) = delete("id", id)
}

@ApplicationScoped
class VPCRepository: PanacheRepository<VPC> {
    fun findByName(name: String) = find("name", name).firstResult<VPC>()
    fun deleteInstance(id: Long) = delete("id", id)
}


Service Class

Kotlin
 
fun listAllEC2Instances(): Uni<List<EC2>> =  ec2Instance.listAll()

    fun listAllAZs(): Uni<List<Azone>> = azRepository.listAll()

    fun listAllRegions(): Uni<List<Region>> = regionRepository.listAll()

    fun listAllSubnets(): Uni<List<Subnet>> = subnetRepository.listAll()

    fun listAllVpcs(): Uni<List<VPC>> = vpcRepository.listAll()

    @Transactional
    fun saveEC2Insance(newEC2: EC2, subnetId: Long):Uni<EC2> {

        return subnetRepository.findById(subnetId)
            .onItem().ifNull().failWith(IllegalArgumentException("Invalid subnet id"))
            .onItem().transformToUni { subnet -> newEC2.subnet = subnet; Uni.createFrom().item(newEC2) }
            .onItem().transformToUni { it -> ec2Instance.persistAndFlush(it) }
            .onItem().invoke { it ->
                LoggerFactory.getLogger(javaClass).info("EC2 instance with id ${it.id} created")
            }

    }

    @Transactional
    fun deleteEC2Instance(id: Long): Uni<Boolean> {

        return ec2Instance.deleteById(id)
            .onItem().invoke { it ->
                LoggerFactory.getLogger(javaClass).info("EC2 instance with id $id deleted")
            }
            .onItem().transform { true }
    }


Resource Class

Kotlin
 
@GraphQLApi
@ApplicationScoped
class ClusterResource {

    @Inject
    lateinit var clusterService: ClusterService

    @Query("listAllEC2s")
    fun listAllEC2Instances(): Uni<List<EC2>>  = clusterService.listAllEC2Instances()

    @Query("listAllAZs")
    fun listAllAZs(): Uni<List<Azone>> = clusterService.listAllAZs()

    @Query("listAllRegions")
    fun listAllRegions(): Uni<List<Region>> = clusterService.listAllRegions()

    @Query("listAllSubnets")
    fun listAllSubnets(): Uni<List<Subnet>> = clusterService.listAllSubnets()

    @Query("listAllVPCs")
    fun listAllVPCs(): Uni<List<VPC>> = clusterService.listAllVpcs()

    @Mutation("addEC2Instance")
    fun addEC2Instance(ec2: EC2, subnetId: Long): Uni<EC2> = clusterService.saveEC2Insance(ec2, subnetId)

    @Mutation("deleteEC2Instance")
    fun deleteEC2Instance(instanceId: Long): Uni<Boolean> = clusterService.deleteEC2Instance(instanceId)
 }


Finally

You can Run the App and access this URL.

GraphQL UI

The GraphQL user interface provides a range of convenient pre-built functionalities, including a command history feature, the ability to list available operations, and an autocomplete feature, among others.

Summary

In this article, we have explored one of the solutions approaches for building Reactive and Native applications, acknowledging that each solution has its own trade-offs and benefits, and we must choose based on our priorities. We have highlighted the high performance and cloud-native support offered by Quarkus and suggested further exploration of these features. Additionally, we have discussed how GraphQL complements this solution by providing many ready-to-use features that make development faster and support low-code, no-code, and open API features. Finally, we have mentioned Kotlin as another excellent technology that offers null safety and eliminates boilerplate code.

The source code can be found on my GitHub. Also, you can reach out to me on LinkedIn for any questions or suggestions.

That’s all for now. Happy Learning!

Database GraphQL Quarkus Kotlin (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Full Lifecycle API Management Is Dead
  • Application Architecture Design Principles
  • NoSQL vs SQL: What, Where, and How
  • Introduction to Containerization

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: