Arquitectura hexagonal

Arquitectura Hexagonal (Puertos y Adaptadores)

La arquitectura hexagonal, también conocida como arquitectura de puertos y adaptadores, es un estilo de diseño de software propuesto por Alistair Cockburn. Su objetivo es aislar el núcleo de la aplicación (la lógica de negocio) del mundo exterior (interfaces de usuario, bases de datos, APIs externas, etc.), creando un diseño más modular, escalable y fácil de probar.


Conceptos clave

  1. Core de la aplicación (Dominios):
    • Es el núcleo que contiene la lógica de negocio, los casos de uso y las reglas de la aplicación.
    • Es completamente independiente de cualquier tecnología o infraestructura externa.
  2. Puertos:
    • Interfaces que definen cómo interactuar con el núcleo de la aplicación.
    • Hay dos tipos:
      • Puertos de entrada: Permiten interactuar con la aplicación desde el exterior (e.g., controladores de una API o eventos).
      • Puertos de salida: Permiten que la aplicación interactúe con servicios externos (e.g., base de datos, APIs de terceros).
  3. Adaptadores:
    • Implementaciones concretas de los puertos. Son responsables de conectar el núcleo con el mundo exterior.
      • Adaptadores de entrada: Transforman las solicitudes externas (API, CLI, UI) en comandos comprensibles para el núcleo.
      • Adaptadores de salida: Implementan las interacciones con servicios externos (e.g., repositorios de datos, APIs externas).
  4. Separación de preocupaciones:
    • Los adaptadores y la infraestructura están separados del núcleo. Esto permite cambiar tecnologías o interfaces externas sin afectar la lógica de negocio.

Beneficios de la arquitectura hexagonal

  1. Independencia tecnológica:
    • Puedes cambiar tecnologías (base de datos, frameworks) sin modificar el núcleo.
  2. Facilidad de pruebas:
    • El núcleo puede ser probado de forma aislada con simulaciones o stubs.
  3. Escalabilidad:
    • Es fácil añadir nuevos adaptadores o integrar nuevos sistemas externos.
  4. Mantenimiento:
    • Reduce la complejidad al desacoplar la lógica de negocio de las dependencias externas.

Ejemplo práctico

Imaginemos un sistema para gestionar órdenes de compra. Implementaremos:

  • Backend (C#): Con un núcleo que maneja órdenes y un adaptador para la base de datos.
  • Frontend (React): Como adaptador de entrada para interactuar con el usuario.

Backend con C# (Puertos y Adaptadores)

Estructura del proyecto

/Core
    - IOrderService.cs (Puerto de entrada)
    - IOrderRepository.cs (Puerto de salida)
    - OrderService.cs (Implementación del servicio)
    - Models/
        - Order.cs
/Infrastructure
    - DatabaseOrderRepository.cs (Adaptador de salida)
/API
    - OrderController.cs (Adaptador de entrada)

Código

  1. Core: Definición de puertos
  2. Puerto de entrada (IOrderService):
    public interface IOrderService
    {
        void CreateOrder(Order order);
        Order GetOrderById(Guid orderId);
    }
    
  3. Puerto de salida (IOrderRepository):
    public interface IOrderRepository
    {
        void Add(Order order);
        Order FindById(Guid orderId);
    }
    
  4. Core: Lógica de negocio
  5. Modelo de Orden:
    public class Order
    {
        public Guid Id { get; set; }
        public string ProductName { get; set; }
        public int Quantity { get; set; }
        public decimal Price { get; set; }
    }
    
  6. Implementación del servicio:
    public class OrderService : IOrderService
    {
        private readonly IOrderRepository _orderRepository;
    
        public OrderService(IOrderRepository orderRepository)
        {
            _orderRepository = orderRepository;
        }
    
        public void CreateOrder(Order order)
        {
            // Validaciones de negocio
            if (order.Quantity <= 0)
                throw new ArgumentException("La cantidad debe ser mayor a cero.");
    
            _orderRepository.Add(order);
        }
    
        public Order GetOrderById(Guid orderId)
        {
            return _orderRepository.FindById(orderId);
        }
    }
    
  7. Infraestructura: Adaptador de salida
  8. Adaptador para la base de datos:
    public class DatabaseOrderRepository : IOrderRepository
    {
        private readonly List<Order> _orders = new List<Order>();
    
        public void Add(Order order)
        {
            _orders.Add(order);
        }
    
        public Order FindById(Guid orderId)
        {
            return _orders.FirstOrDefault(o => o.Id == orderId);
        }
    }
    
  9. API: Adaptador de entrada
  10. Controlador:
    [ApiController]
    [Route("api/orders")]
    public class OrderController : ControllerBase
    {
        private readonly IOrderService _orderService;
    
        public OrderController(IOrderService orderService)
        {
            _orderService = orderService;
        }
    
        [HttpPost]
        public IActionResult CreateOrder([FromBody] Order order)
        {
            _orderService.CreateOrder(order);
            return Ok("Orden creada con éxito.");
        }
    
        [HttpGet("{id}")]
        public IActionResult GetOrderById(Guid id)
        {
            var order = _orderService.GetOrderById(id);
            return order != null ? Ok(order) : NotFound();
        }
    }
    

Frontend con React (Adaptador de entrada)

Estructura del proyecto

/components
    - OrderForm.jsx
    - OrderList.jsx
/services
    - api.js

Código

  1. Servicio para interactuar con el backend (api.js):
    const BASE_URL = "http://localhost:5000/api/orders";
    
    export const createOrder = async (order) => {
        const response = await fetch(`${BASE_URL}`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(order),
        });
        return response.json();
    };
    
    export const getOrderById = async (id) => {
        const response = await fetch(`${BASE_URL}/${id}`);
        return response.json();
    };
    
  2. Formulario para crear órdenes (OrderForm.jsx):
    import React, { useState } from "react";
    import { createOrder } from "../services/api";
    
    const OrderForm = () => {
        const [productName, setProductName] = useState("");
        const [quantity, setQuantity] = useState(1);
        const [price, setPrice] = useState(0);
    
        const handleSubmit = async (e) => {
            e.preventDefault();
            const order = { productName, quantity, price };
            await createOrder(order);
            alert("Orden creada con éxito.");
        };
    
        return (
            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    placeholder="Nombre del producto"
                    value={productName}
                    onChange={(e) => setProductName(e.target.value)}
                />
                <input
                    type="number"
                    placeholder="Cantidad"
                    value={quantity}
                    onChange={(e) => setQuantity(e.target.value)}
                />
                <input
                    type="number"
                    placeholder="Precio"
                    value={price}
                    onChange={(e) => setPrice(e.target.value)}
                />
                <button type="submit">Crear Orden</button>
            </form>
        );
    };
    
    export default OrderForm;
    

Ventajas en este ejemplo

  1. Backend:
    • La lógica de negocio está aislada (OrderService) y puede probarse fácilmente con mocks.
    • Cambiar la base de datos o usar un servicio externo solo requiere modificar DatabaseOrderRepository.
  2. Frontend:
    • El adaptador para interactuar con el backend (api.js) desacopla los detalles de implementación del resto del código.

Para saber más:

https://salesystems.es/arquitectura-hexagonal/

https://www.hackio.com/blog/que-es-arquitectura-hexagonal-en-programacion

https://medium.com/@edusalguero/arquitectura-hexagonal-59834bb44b7f

El algoritmo SHA256 paso a paso

https://sha256algorithm.com/

Vía microsiervos:

https://www.microsiervos.com/archivo/seguridad/algoritmo-sha-256-explicado-visualizado-paso-a-paso-bit-a-bit.html

Si la definición del SHA-256 suena un poco rara y marciana es porque si no estás muy puesto en criptografía y las matemáticas relacionadas lo es. En lenguaje llano se podría decir que es una función que transforma un fichero cualquiera en un valor de longitud fija único, llamado hash. Y ese valor, que es una especie de suma de control, valor de verificación o firma única tiene ciertas propiedades interesantes en múltiples aplicaciones en criptografía y seguridad. La utilidad es que a partir de ese valor, que en el caso del SHA-256 son 256 bits, es imposible generar el documento original, pero sobre todo es prácticamente imposible encontrar otro documento que produzca el mismo hash si no es buscando «por fuerza bruta», debido a que es una especie de función en un sólo sentido. Y eso no siempre es posible, lo cual garantiza su seguridad.

Funciones para practicar

1.- Crear una función triple que nos devuelva el triple de un número. Ej: triple(5)–>15 triple(0)–>0
2.- Crear una función nombreCompleto que pasándole un nombre y un apellido nos devuelva una cadena con nombre y apellido juntos separados por un espacio: nombreCompleto(‘ana’,’pi’)->’ana pi’
3.- Crear una función areaCirculo que le pasemos el radio de un círculo y nos devuelva el área, que es 2*3.1416*r*r ej: areaCirculo(1)–>6,2832
4.- Crear una función triplicar que le pasemos una cadena y nos la devuelva repetida tres veces: triplocar(‘hola’)->’holaholahola’
5.- Crear una función perímetro a la que le pasamos dos lados y nos devuelve el perímetro de un rectángulo, que es lado1*2+lado2*2 Ej: perimetro(2,3)–>10
6.- Crear una función que me devuelva el mayor elemento del array
7.- Función que nos sume las posiciones pares por un lado y las impares por otro. Nos devuelve un array con [pares,impares]
8.- Función que nos cuente las vocales de una cadena
9.- Función que nos cuente las consonantes
10.- Función que nos diga si un número es capicua
11.- Función que nos diga si una frase es capicua (deberíamos eliminar los espacios)
12.- Función que nos elimine espacios repetidos dentro de la cadena
13.- Función que dada una cadena nos ponga las vocales en minúsculas y las consonantes en mayúsculas
14.- Función que nos devuelva la tirada de dos dados en un array: [1,5]
15.- Función que nos cuenta las apariciones de una cadena dentro de otra
16.- Función que nos convierta de minutos a segundos
17.- Función que nos diga,dada una edad, cuantos días ha vivido esa persona (sin contar bisiestos)
18.- Función que dados dos números nos diga si tienen la misma paridad
19.- Función a la que le pasemos un array y un número y nos devuelva un array con los numeros menores que el número dado
20.- Función a la que le pasemos una cadena y una longitud y nos elimine las palabras que sean de mayor longitud que la dada.


// Función a la que le pasemos una cadena y una longitud y nos elimine las palabras que sean de mayor longitud que la dada.
// eliminarPorLongitud('hola que tal',3)-->'que tal'
// eliminarPorLongitud('hola que tal estamos',4)-->'hola que tal'

function eliminarPorLongitud (cadena, longitud) {
// recorrer las palabras y mirar su longitud
  const palabras = cadena.split(' ')
  let res = ''
  for (let i = 0; i < palabras.length; i++) {
    if (palabras[i].length <= longitud) {
      res += palabras[i] + ' '
    }
  }
  return res.trim()
}

function eliminarPorLongitud2 (cadena, longitud) {
  // recorrer las palabras y mirar su longitud
  const palabras = cadena.split(' ')
  const res = []
  for (let i = 0; i < palabras.length; i++) {
    if (palabras[i].length <= longitud) {
      res.push(palabras[i])
    }
  }
  return res.join(' ')
}

Páginas para aprender programación

Para niños que empiezan:

https://code.org/

Programando en bloques y su correspondencia en javascript:

https://developers.google.com/blockly/

Proyectos hechos con el anterior:

https://www.madewithcode.com/projects/

App que explica conceptos básicos:

https://grasshopper.app/es_419/

Para aprender lenguajes de programación gratis:

https://www.codecademy.com/

Más cursos gratis:

https://www.codeavengers.com/

Orientado a Python y big data:

https://www.datacamp.com/

Completísimos cursos de HTML, JS y SQL:

https://es.khanacademy.org/computing/computer-programming

Una aplicación para repasar día a día:

Enki

¿Qué lenguaje de programación usar?

¿Qué lenguaje de programación aprender?

Manuales para empezar con javascript:

Introducción a la programación

Recopilatorios de tutoriales:

https://hackr.io/

Algunas ideas para empezar a programar de adulto (está muy bien):

https://www.xataka.com/makers/como-empezar-a-aprender-programacion-consejos-y-recursos-para-hacerlo-de-adulto