Arquitectura Hexagonal en Kotlin: pureza y flexibilidad del código

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Arquitectura Hexagonal en Kotlin: cuando el código respira libremente

\n\n

Imagina que estás construyendo una casa. Puedes verter los cimientos, levantar las paredes y colocar las ventanas de inmediato. O puedes diseñar primero la estructura, instalar las instalaciones y solo después ocuparte del acabado. En el mundo de la programación, el segundo enfoque se llama arquitectura hexagonal (también conocida como Ports & Adapters). Y Kotlin, con su concisión y su potente sistema de tipos, es el lenguaje ideal para dar vida a este concepto.

\n\n

Este artículo no es un aburrido manual. Es una guía práctica que mostrará cómo la Arquitectura Hexagonal en Kotlin transforma tu código en un sistema flexible, testeable e independiente de servicios externos. Analizaremos un ejemplo desde cero, escribiremos código real y veremos cómo funciona en la práctica.

\n\n

¿Qué es la arquitectura hexagonal y por qué la necesitas?

\n\n

Alistair Cockburn, quien acuñó este término, comparó la aplicación con un hexágono. En el centro está tu lógica de negocio. En los bordes están los puertos (interfaces) a través de los cuales se comunica con el mundo exterior. Los adaptadores son implementaciones de estas interfaces para tecnologías específicas: base de datos, framework web, cola de mensajes.

\n\n

Idea clave: la lógica de negocio no sabe nada sobre cómo se almacenan los datos o qué framework se utiliza. Simplemente llama a los métodos de los puertos. ¿Quieres reemplazar PostgreSQL por MongoDB? Simplemente escribe un nuevo adaptador. La lógica de negocio ni siquiera notará el cambio.

\n\n

En Kotlin, esto es especialmente conveniente gracias a las interfaces con implementaciones por defecto, las clases selladas y el estilo funcional. Pasemos a la acción.

\n\n

Diseñando el sistema: ejemplo de una tienda en línea

\n\n

Supongamos que estamos escribiendo un microservicio para la gestión de pedidos. En el centro está OrderService, que crea pedidos y verifica la disponibilidad de productos. El mundo exterior son un controlador HTTP y una base de datos.

\n\n

Así es como se ve la estructura del proyecto:

\n\n
src/\n├── domain/          # Núcleo (lógica de negocio)\n│   ├── model/\n│   ├── port/\n│   └── service/\n├── adapter/\n│   ├── input/       # Adaptadores de entrada (controladores)\n│   └── output/      # Adaptadores de salida (repositorios)\n└── Application.kt   # Punto de entrada\n
\n\n

Paso 1. Núcleo: modelos y puertos

\n\n

Empecemos con el modelo de pedido. En Kotlin será un 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

Ahora definamos los puertos — interfaces a través de las cuales el núcleo se comunica con el mundo exterior. Tendremos dos puertos: uno para obtener datos sobre productos y otro para guardar 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

Observa: en ninguna parte se menciona la base de datos, HTTP o frameworks. Lógica de negocio pura.

\n\n

Paso 2. Servicio de negocio

\n\n

Ahora escribamos OrderService, que utiliza estos puertos. En Kotlin, pasamos las dependencias a través del constructor — esto se llama inyección de dependencias (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        // Verificamos la disponibilidad de cada producto\n        for (item in items) {\n            if (!productRepository.checkA                

Blogs

Book Recommendations