Arquitectura Hexagonal en C++: Código Limpio Sin Dolor

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 C++: Cómo Dejar de Temer los Cambios y Amar las Pruebas

\n\n

¿Alguna vez ha reescrito un módulo de base de datos y luego se ha dado cuenta de que necesita cambiar la mitad de la lógica de negocio? ¿O ha intentado cubrir con pruebas unitarias una clase que arrastra tres toneladas de dependencias del mundo exterior? Si es así, bienvenido al club. Afortunadamente, tenemos una herramienta que resuelve estos problemas de una vez por todas: la arquitectura hexagonal (también conocida como arquitectura de Puertos y Adaptadores).

\n\n

En este artículo, analizaremos cómo aplicar este poderoso concepto en C++. Verá no esquemas abstractos, sino código real que puede usar ahora mismo. Y sí, lo haremos sin fanatismo, de manera que ni un proyecto heredado explote y el código nuevo sea limpio.

\n\n

¿Qué es la Arquitectura Hexagonal (y qué tiene que ver el hexágono)?

\n\n

Imagine el núcleo de su aplicación: es la lógica de negocio pura que no sabe nada sobre bases de datos, API REST o interfaz gráfica. Alrededor de este núcleo se encuentran los puertos (interfaces), y más allá, los adaptadores (implementaciones concretas). Visualmente, esto se asemeja a un hexágono, de ahí el nombre.

\n\n

La idea clave: las dependencias apuntan hacia adentro. El mundo exterior depende del núcleo, no al revés. Esto permite cambiar la base de datos, el protocolo de comunicación o la interfaz de usuario sin modificar la lógica de negocio. ¿Suena como un sueño? Veamos cómo implementarlo en C++.

\n\n

El Problema Clásico: Alto Acoplamiento

\n\n

Así es como se ve a menudo el código C++ típico:

\n\n
class OrderService {\npublic:\n    void createOrder(const OrderData& data) {\n        // Lógica de negocio\n        if (data.items.empty()) throw std::runtime_error("El pedido está vacío");\n\n        // Y aquí está el problema: dependencia directa de la base de datos\n        Database db("localhost", 5432);\n        db.connect();\n        db.saveOrder(data);\n        db.disconnect();\n        \n        // Y también del servicio de correo electrónico\n        EmailSender sender("smtp.example.com");\n        sender.sendInvoice(data);\n    }\n};
\n\n

¿Qué está mal aquí? Todo. No puede probar createOrder sin una base de datos real y un servidor de correo. Si mañana decide migrar de PostgreSQL a MongoDB, tendrá que reescribir OrderService. Y si el servicio de correo electrónico se vuelve asíncrono a través de una cola, de nuevo cambios en la lógica de negocio.

\n\n

Resolviendo el Problema con la Arquitectura Hexagonal

\n\n

Paso 1: Definir los Puertos (Interfaces)

\n\n

Un puerto es un contrato que define qué necesita el núcleo del mundo exterior, pero no cómo se implementa.

\n\n
// port/OrderRepository.h\nclass OrderRepository {\npublic:\n    virtual ~OrderRepository() = default;\n    virtual void save(const Order& order) = 0;\n    virtual std::optional<Order> findById(const std::string& id) = 0;\n};\n\n// port/NotificationService.h\nclass NotificationService {\npublic:\n    virtual ~NotificationService() = default;\n    virtual void sendInvoice(const Order& order) = 0;\n};
\n\n

Observe: son clases abstractas puras. Sin dependencias de frameworks, bibliotecas o servicios externos. Solo entidades de negocio.

\n\n

Paso 2: Escribir el Núcleo (Lógica de Negocio)

\n\n

Ahora nuestro servicio depende solo de interfaces, no de implementaciones concretas:

\n\n
// core/OrderService.h\nclass OrderService {\npublic:\n    OrderService(OrderRepository& repo, NotificationService& notifier)\n        : repository_(repo), notifier_(notifier) {}\n\n    void createOrder(const OrderData& data) {\n        // Validación — lógica de negocio pura\n        if (data.items.empty()) {\n            throw std::invalid_argument("El pedido no puede estar vacío");\n        }\n\n        Order order = Order::create(data);\n        repository_.save(order);\n        notifier_.sendInvoice(order);\n    }\n\nprivate:\n    OrderRepository& repository_;\n    NotificationServ                

Blogs

Book Recommendations