Los patrones de microservicios son soluciones recurrentes a problemas comunes que surgen al diseñar, implementar y mantener arquitecturas basadas en microservicios. Estos patrones ayudan a abordar desafíos específicos y a tomar decisiones informadas para construir sistemas escalables, mantenibles y flexibles.

Ya os contamos en un post anterior qué es una arquitectura de microservicios y veíamos desde algunas más tradicionales como MVC hasta Clean Architecture, con toda la importancia que tiene hoy en día en las arquitecturas de microservicios, para terminar con el patrón Database per Microservice.

Seguimos esta serie de patrones de arquitectura de microservicios con la segunda entrega, donde vamos a continuar con los patrones de arquitectura enfocados a la organización y estructura de microservicios.

Continuamos con DDD, Clean Architecture y con arquitectura hexagonal, estos 3 patrones pueden ir de la mano, así que veremos qué nos pueden aportar en conjunto. Finalmente, veremos la arquitectura serverless en microservicios.

Organización y estructura de microservicios

DDD

El DDD, diseño orientado al dominio (Domain-Driven Design), es un enfoque para desarrollar software que se centra en modelar el dominio del problema de manera exhaustiva y enriquecer la comunicación y colaboración entre los equipos de desarrollo y los expertos del dominio, como usuarios y stakeholders.

DDD proporciona un conjunto de principios y patrones que ayudan a crear software que refleje con precisión las reglas y procesos del negocio, lo que conduce a soluciones más efectivas y mantenibles.

El núcleo de DDD se basa en las siguientes ideas clave:

Veamos un ejemplo:

Supongamos que estamos desarrollando un ecommerce y queremos aplicar DDD para modelar el proceso de compra.

A la hora de particionar los dominios, pueden seguir distintas estrategias (por ejemplo: Entidades lógicas Vs Procesos de negocio) y tener diferentes consideraciones (Por ejemplo: Los subdominios del negocio global, sus características relacionadas, equipo de desarrollo, familia tecnológica, limitación de dependencias…)

DDD ofrece una forma estructurada de abordar problemas complejos de dominio y brinda claridad en la comunicación entre equipos de desarrollo y expertos del negocio. El enfoque en el modelado del dominio y la colaboración entre diferentes partes interesadas contribuyen a la creación de software de alta calidad y más alineado con las necesidades del negocio.

Aquí, un diagrama del ejemplo simplificado y acotado, se podría adaptar y expandir métodos y eventos según las necesidades de la aplicación de e-commerce y del modelo de dominio específico.

Se pueden observar los distintos dominios que a priori saldrían con sus entidades.

Distintos dominios que a priori saldrían con sus entidades.

Arquitectura hexagonal

La arquitectura hexagonal propone que nuestro dominio sea el núcleo de las capas y que este no se acople a nada externo. En lugar de hacer uso explícito y mediante el principio de inversión de dependencias, nos acoplamos a contratos (interfaces o puertos) y no a implementaciones concretas.

También conocida como arquitectura de puertos y adaptadores, el diseño se basa en la separación de las preocupaciones y la independencia de las capas. Se busca aislar el núcleo de la aplicación, donde reside la lógica de negocio, de las interacciones con componentes externos como interfaces de usuario y bases de datos.

A grandes rasgos, y sin mucho detalle, lo que propone es que nuestro núcleo sea visto como una API con unos contratos bien especificados. Definiendo puertos o puntos de entrada e interfaces (adaptadores) para que otros módulos (UI, BBDD, Test) puedan implementarlas y comunicarse con la capa de negocio sin que esta deba saber el origen de la conexión.

En un e-commerce, esta arquitectura es esencial para garantizar la flexibilidad, mantenibilidad y evolución del sistema.

Esto es lo llamado puertos y adaptadores, que podrían ser definidos de la siguiente manera:

Componentes clave:

Un ejemplo de código sería:

  1. Puerto

Puerto de servicio de gestión de productos:

public interface ProductService {
    Product createProduct(ProductDTO productDTO);
    List<Product> getAllProducts();
}
  1. Núcleo del dominio

Entidad de producto:

public class Product {
    private String id;
    private String name;
    private double price;

    // Constructores, getters y setters
}

DTO para creación de productos:

public class ProductDTO {
    private String name;
    private double price;

    // Constructores, getters y setters
}
  1. Adaptador

Controlador de productos:

@RestController
@RequestMapping("/products")
public class ProductController {
    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @PostMapping
    public ResponseEntity<Product> createProduct(@RequestBody ProductDTO productDTO) {
        Product createdProduct = productService.createProduct(productDTO);
        return ResponseEntity.status(HttpStatus.CREATED).body(createdProduct);
    }

    @GetMapping
    public ResponseEntity<List<Product>> getAllProducts() {
        List<Product> products = productService.getAllProducts();
        return ResponseEntity.ok(products);
    }
}
  1. Implementación de servicio
@Service
public class ProductServiceImpl implements ProductService {
    private final ProductRepository productRepository;

    public ProductServiceImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override
    public Product createProduct(ProductDTO productDTO) {
        Product product = new Product();
        product.setName(productDTO.getName());
        product.setPrice(productDTO.getPrice());

        // Lógica adicional, validaciones, etc.

        return productRepository.save(product);
    }

    @Override
    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }
}
  1. Importante la organización

Un buen ejemplo de cómo tendría que organizarse en paquetes sería:

Organización en paquetes

Hexagonal+DDD+Clean Architecture

Aplicar la arquitectura hexagonal, el Domain-Driven Design (DDD) y la Clean Architecture conjuntamente puede resultar en un diseño de software altamente eficiente y orientado al negocio.

Vamos a explorar cómo estos tres enfoques se combinan para crear una estructura sólida y modular.

Relación entre arquitectura hexagonal, DDD y Clean Architecture

La arquitectura hexagonal y la Clean Architecture comparten principios de diseño similares. Ambas enfatizan la separación de las responsabilidades y la creación de un código flexible y mantenible.

La arquitectura hexagonal y DDD se complementan bien, ya que DDD proporciona una guía sólida para modelar el núcleo del dominio, mientras que la arquitectura hexagonal ayuda a implementar esa lógica de dominio en un diseño flexible y desacoplado.

En resumen, estas tres metodologías y enfoques se entrelazan para crear sistemas de software que son comprensibles, mantenibles y adaptables, al tiempo que reflejan fielmente las complejidades y reglas de negocio del dominio que abordan.

Aplicación conjunta

Al combinar estos enfoques, obtienes:

  1. Independencia total. Ell núcleo del dominio está completamente aislado de las capas externas, lo que permite cambios en las tecnologías sin afectar el negocio y viceversa.
  2. Modelado de dominio. DDD permite representar las reglas del negocio y los conceptos clave del dominio en el código, creando un lenguaje compartido entre desarrolladores y expertos en el negocio.
  3. Separación de preocupaciones. La separación de capas y la definición de interfaces claras aseguran una clara separación de las preocupaciones y una estructura modular.
  4. Mantenibilidad. La estructura modular y la independencia de las tecnologías externas facilitan la evolución y el mantenimiento del sistema con el tiempo.

En resumen, aplicar conjuntamente la arquitectura hexagonal, el Domain-Driven Design y la Clean Architecture proporciona una base sólida para el diseño de software. Esta combinación fomenta la flexibilidad, el enfoque en el negocio y la capacidad de adaptación, lo que resulta en aplicaciones robustas y mantenibles a largo plazo.

Serverless

La tecnología de computación serverless ha transformado la forma en que las aplicaciones se despliegan y escalan en la nube.

Aunque el término "serverless" puede ser un poco engañoso (ya que sigue habiendo servidores en funcionamiento), se refiere a una abstracción de la infraestructura subyacente que permite a los desarrolladores enfocarse en el código y la lógica de su aplicación, en lugar de administrar servidores y recursos.

¿Qué es la computación serverless?

La computación serverless es un modelo de desarrollo y despliegue en la nube en el cual el proveedor de la nube es responsable de gestionar la infraestructura subyacente.

Los desarrolladores simplemente cargan su código en la nube y la plataforma se encarga de ejecutarlo en respuesta a eventos, como solicitudes HTTP, cambios en bases de datos o cargas en colas.

Esto permite que las aplicaciones escalen automáticamente según la demanda, sin necesidad de preocuparse por el aprovisionamiento de servidores.

Ventajas de la computación serverless:

Casos de uso:

  1. Aplicaciones web.

Serverless es ideal para aplicaciones web con carga variable, ya que pueden escalar automáticamente según el tráfico.

  1. Procesamiento de datos.

Procesar datos en lotes o en tiempo real es eficiente con serverless, ya que puedes ejecutar funciones específicas para cada tarea.

  1. Microservicios.

Cada microservicio puede ser implementado como una función serverless, lo que facilita el mantenimiento y la escalabilidad independiente.

Desafíos:

Ejemplo de computación serverless:

Supongamos que tienes una tienda en línea y deseas realizar una acción cada vez que se realiza un pedido, como enviar un correo electrónico de confirmación al cliente y actualizar el inventario. Puedes aprovechar la computación serverless para manejar estas acciones de manera eficiente.

// Función Lambda para procesar pedidos
exports.handler = async (event) => {

    // Parsear el evento (que podría ser un pedido en formato JSON)
    const order = JSON.parse(event.body);

    // Simular el envío de un correo electrónico de confirmación
    await sendConfirmationEmail(order.customerEmail, order.orderId);

    // Actualizar el inventario
    await updateInventory(order.products);

    // Respuesta exitosa
    return {
        statusCode: 200,
        body: JSON.stringify({ message: 'Pedido procesado exitosamente' })
    };

};

// Función para enviar un correo electrónico de confirmación
async function sendConfirmationEmail(email, orderId) {

    // Lógica para enviar el correo electrónico
    console.log(`Correo electrónico de confirmación enviado a ${email} para el pedido ${orderId}`);

}

// Función para actualizar el inventario
async function updateInventory(products) {

    // Lógica para actualizar el inventario de los productos
    console.log('Inventario actualizado para los productos:', products);

}

¿Cómo funciona?

  1. Un cliente realiza un pedido en tu tienda en línea.
  2. Tu aplicación crea un evento que contiene los detalles del pedido y lo envía a la función Lambda.
  3. La función Lambda procesa el pedido, envía un correo electrónico de confirmación al cliente y actualiza el inventario de productos.
  4. La función Lambda devuelve una respuesta indicando que el pedido ha sido procesado.

Este ejemplo muestra cómo puedes aprovechar la computación serverless para manejar tareas específicas en tu aplicación de ecommerce sin la necesidad de mantener servidores en funcionamiento todo el tiempo. Esto ahorra recursos y permite que te enfoques en brindar una excelente experiencia al cliente.

¿Qué os ha parecido? ¿Tenéis alguna duda? Podéis dejarnos un comentario. Además, si os interesa este tema, en este post hablamos comunicación y coordinación entre microservicios, ¡esperemos que os sea útil!

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.