Arquitetura Hexagonal em Kotlin: pureza e flexibilidade do código

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Arquitetura Hexagonal em Kotlin: quando o código respira livremente

\n\n

Imagine que você está construindo uma casa. Você pode despejar a fundação, erguer as paredes e colocar as janelas imediatamente. Ou pode primeiro projetar a estrutura, instalar as utilidades e só então cuidar do acabamento. No mundo da programação, a segunda abordagem é chamada de arquitetura hexagonal (também conhecida como Ports & Adapters). E Kotlin, com sua concisão e sistema de tipos poderoso, é a linguagem ideal para dar vida a esse conceito.

\n\n

Este artigo não é um tutorial chato. É um guia prático que mostrará como a Arquitetura Hexagonal em Kotlin transforma seu código em um sistema flexível, testável e independente de serviços externos. Vamos analisar um exemplo do zero, escrever código real e ver como isso funciona na prática.

\n\n

O que é arquitetura hexagonal e por que você precisa dela?

\n\n

Alistair Cockburn, que cunhou o termo, comparou a aplicação a um hexágono. No centro está sua lógica de negócios. Nas bordas estão as portas (interfaces) através das quais ela se comunica com o mundo externo. Os adaptadores são implementações dessas interfaces para tecnologias específicas: banco de dados, framework web, fila de mensagens.

\n\n

Ideia chave: a lógica de negócios não sabe como os dados são armazenados ou qual framework está sendo usado. Ela simplesmente chama métodos das portas. Quer substituir PostgreSQL por MongoDB? Basta escrever um novo adaptador. A lógica de negócios nem perceberá a troca.

\n\n

Em Kotlin, isso é especialmente conveniente graças a interfaces com implementações padrão, classes sealed e estilo funcional. Vamos ao que interessa.

\n\n

Projetando o sistema: exemplo de loja online

\n\n

Suponha que estamos escrevendo um microsserviço para gerenciamento de pedidos. No centro está OrderService, que cria pedidos e verifica a disponibilidade de produtos. O mundo externo é um controlador HTTP e um banco de dados.

\n\n

Veja como fica a estrutura do projeto:

\n\n
src/\n├── domain/          # Núcleo (lógica de negócios)\n│   ├── model/\n│   ├── port/\n│   └── service/\n├── adapter/\n│   ├── input/       # Adaptadores de entrada (controladores)\n│   └── output/      # Adaptadores de saída (repositórios)\n└── Application.kt   # Ponto de entrada\n
\n\n

Passo 1. Núcleo: modelos e portas

\n\n

Vamos começar com o modelo de pedido. Em Kotlin, será um data class:

\n\n
// domain/model/Order.kt\ndata class Order(\n    val id: String,\n    val customerId: String,\n    val items: List<OrderItem>,\n    val status: OrderStatus\n)\n\nenum class OrderStatus {\n    PENDING, CONFIRMED, SHIPPED, CANCELLED\n}\n\ndata class OrderItem(\n    val productId: String,\n    val quantity: Int\n)\n
\n\n

Agora definimos as portas — interfaces através das quais o núcleo se comunica com o mundo externo. Teremos duas portas: uma para obter dados sobre produtos e outra para salvar pedidos.

\n\n
// domain/port/ProductRepository.kt\ninterface ProductRepository {\n    fun checkAvailability(productId: String, quantity: Int): Boolean\n}\n\n// domain/port/OrderRepository.kt\ninterface OrderRepository {\n    fun save(order: Order): Order\n    fun findById(id: String): Order?\n}\n
\n\n

Observe: em nenhum lugar há menção a banco de dados, HTTP ou frameworks. Lógica de negócios pura.

\n\n

Passo 2. Serviço de negócios

\n\n

Agora escrevemos OrderService, que usa essas portas. Em Kotlin, passamos dependências pelo construtor — isso é chamado de injeção de dependências (DI).

\n\n
// domain/service/OrderService.kt\nclass OrderService(\n    private val productRepository: ProductRepository,\n    private val orderRepository: OrderRepository\n) {\n    fun createOrder(customerId: String, items: List<OrderItem>): Order {\n        // Verifica a disponibilidade de cada produto\n        for (item in items) {\n            if (!productRepository.checkA                

Blogs

Book Recommendations