Hands on Hexagonal architecture Kotlin

Hands on Hexagonal architecture Kotlin

2022, Nov 11    

Hexagonal architecture, also known as “ports and adapters” or “onion architecture,” is a software design pattern that separates the internal structure of a software system from its external interactions. This architecture follows the principles of separation of concerns and single responsibility, allowing for a cleaner, more maintainable codebase.

In this tutorial, we will be creating a Kotlin project that follows hexagonal architecture principles. We will start by setting up the project structure and creating a basic “hello world” example to demonstrate how the different parts of the system interact.

Setting up the project structure First, let’s create a new Kotlin project using your preferred build tool (e.g., Gradle or Maven). For the purposes of this tutorial, we will be using Gradle.

Once the project is created, we can start by organizing the package structure. In a hexagonal architecture, the core of the system is represented by the “domain” layer. This layer contains the business logic and the entities that represent the core concepts of the system.

src
 └── main
    └── kotlin
        └── com
            └── example
                ├── domain
                ├── infrastructure
                ├── interfaces
                └── usecases

The “infrastructure” layer contains the implementation of the external interfaces and dependencies of the system, such as databases, APIs, and external services.

The “interfaces” layer contains the interface definitions for the external interactions of the system. These interfaces could be REST APIs, command line interfaces, or any other way in which the system communicates with the outside world.

The “usecases” layer contains the implementation of the business logic, using the entities and services from the domain layer and the interfaces from the infrastructure layer.

Creating the hello world example Now that we have the basic project structure set up, let’s create a simple “hello world” example to demonstrate how the different parts of the system interact.

First, let’s create a simple entity in the domain layer to represent a person.

// src/main/kotlin/com/example/domain/Person.kt

data class Person(val name: String)

Next, let’s create a service in the domain layer that will greet a person.

// src/main/kotlin/com/example/domain/GreetingService.kt

class GreetingService {
    fun greet(person: Person): String {
        return "Hello, ${person.name}!"
    }
}

Now, let’s create an interface in the interfaces layer that will define the external interactions of the system. In this case, we will create a REST API that allows the user to input a person’s name and receive a greeting in response.

// src/main/kotlin/com/example/interfaces/GreetingAPI.kt

@Path("/greeting")
class GreetingAPI {
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    fun greet(person: Person): String {
        return "Hello, ${person.name}!"
    }
}

Now, let’s create an implementation of the GreetingAPI in the infrastructure layer that uses the GreetingService from the domain layer to generate the greeting.

// src/main/kotlin/com/example/infrastructure/GreetingAPIImpl.kt

class GreetingAPIImpl(private val greetingService: GreetingService) : GreetingAPI {
    override fun greet(person: Person): String {
        return greetingService.greet(person)
    }
}

Finally, let’s create a use case in the usecases layer that uses the GreetingAPI interface to greet a person.

// src/main/kotlin/com/example/usecases/GreetingUseCase.kt

class GreetingUseCase(private val greetingAPI: GreetingAPI) {
    fun greet(person: Person): String {
        return greetingAPI.greet(person)
    }
}

With these pieces in place, we have a basic Kotlin project following hexagonal architecture principles. The domain layer contains the business logic and core entities, the infrastructure layer contains the implementation of external dependencies and interfaces, the interfaces layer contains the interface definitions for external interactions, and the usecases layer contains the implementation of the business logic using the interfaces and domain layers.

To test this example, you will need to set up a web server to handle incoming HTTP requests and route them to the GreetingAPI. This can be done using a library like Javalin or Spark.

Once the server is set up, you can send a POST request to the /greeting endpoint with a JSON payload containing a person’s name, and you should receive a greeting in response.

I hope this tutorial has been helpful in showing how to create a Kotlin project following hexagonal architecture principles. This architecture can help to create a cleaner, more maintainable codebase by separating the internal structure of the system from its external interactions.