Introdução: por que Kotlin é a linguagem ideal para microsserviços
O desenvolvimento moderno de backend está cada vez mais se afastando de soluções monolíticas em favor da arquitetura de microsserviços. Kotlin, sendo uma linguagem estaticamente tipada que roda sobre a JVM, oferece aos desenvolvedores uma combinação única de concisão, segurança e desempenho. Ao contrário de Java, Kotlin permite escrever código mais expressivo e seguro sem boilerplate, o que é crucial ao construir sistemas distribuídos complexos.
Neste artigo, abordaremos o ciclo completo de criação de um microsserviço em Kotlin: desde a escolha do framework até a implantação em um contêiner. Você aprenderá a usar Ktor para o servidor HTTP, Koin para injeção de dependências e Exposed para trabalhar com banco de dados. Todos os exemplos de código estão prontos para uso em projetos reais.
1. Projetando a arquitetura do microsserviço
O primeiro passo é definir os limites do serviço. Ao contrário de um monolito, cada microsserviço é responsável por uma capacidade de negócio. Vamos considerar o exemplo de um serviço de gerenciamento de usuários (User Service).
Estrutura do projeto
A estrutura recomendada para um microsserviço Kotlin:
user-service/
├── src/
│ ├── main/kotlin/com/example/userservice/
│ │ ├── application/ # Camada de aplicação (casos de uso)
│ │ ├── domain/ # Modelos de domínio
│ │ ├── infrastructure/ # Repositórios, BD, APIs externas
│ │ └── presentation/ # Controladores (rotas Ktor)
│ └── resources/
│ └── application.conf
└── build.gradle.kts
Essa arquitetura segue os princípios da Clean Architecture e do Domain-Driven Design, facilitando os testes e a manutenção.
Escolha da stack tecnológica
- Framework HTTP: Ktor (leve, assíncrono, coroutines)
- DI: Koin (simples, sem reflexão)
- ORM: Exposed (DSL nativa Kotlin para SQL)
- Banco de dados: PostgreSQL
- Containerização: Docker + Docker Compose
2. Implementação do microsserviço em Kotlin com Ktor e Koin
Configuração do build.gradle.kts
Adicionamos as dependências necessárias:
plugins {
kotlin("jvm") version "1.9.22"
id("io.ktor.plugin") version "2.3.7"
kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
implementation("io.ktor:ktor-server-core:2.3.7")
implementation("io.ktor:ktor-server-netty:2.3.7")
implementation("io.ktor:ktor-server-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
implementation("org.jetbrains.exposed:exposed-core:0.44.1")
implementation("org.jetbrains.exposed:exposed-dao:0.44.1")
implementation("org.jetbrains.exposed:exposed-jdbc:0.44.1")
implementation("org.postgresql:postgresql:42.6.0")
implementation("io.insert-koin:koin-ktor:3.5.0")
}
Criação do modelo de domínio e repositório
// domain/User.kt
data class User(
val id: Long,
val name: String,
val email: String
)
// infrastructure/UserRepository.kt
interface UserRepository {
suspend fun getAll(): List<User>
suspend fun getById(id: Long): User?
suspend fun create(user: User): User
}
Implementação do repositório com Exposed
// infrastructure/UserRepositoryImpl.kt
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
object UsersTable : Table("users") {
val id = long("id").autoIncrement()
val name = varchar("name", 255)
val email = varchar("email", 255)
override val primaryKey = PrimaryKey(id)
}
class UserRepositoryImpl(private val database: Database) : UserRepository {
override suspend fun getAll(): List<User> =
```