Introducción
La arquitectura hexagonal, también conocida como arquitectura de puertos y adaptadores, fue introducida por Alistair Cockburn para abordar el problema del acoplamiento entre el sistema y sus dependencias externas. Esta arquitectura se centra en la separación de preocupaciones, permitiendo que el dominio de la aplicación permanezca independiente de la infraestructura y la presentación. Esto no solo mejora la mantenibilidad del código, sino que también facilita las pruebas y la evolución del sistema.
Principios Fundamentales
-
Independencia del Dominio: El núcleo de la aplicación, que contiene la lógica de negocio, debe estar separado de las tecnologías específicas de infraestructura (bases de datos, APIs externas, interfaces de usuario, etc.).
-
Puertos y Adaptadores: Los puertos son interfaces que representan las interacciones con el dominio, mientras que los adaptadores son implementaciones concretas de esos puertos. Esto permite una comunicación clara y desacoplada entre el dominio y el exterior.
-
Testabilidad: La separación entre el dominio y la infraestructura facilita la creación de pruebas unitarias y de integración, ya que se puede simular el comportamiento de las dependencias externas.
-
Flexibilidad y Escalabilidad: Esta arquitectura permite añadir o cambiar adaptadores sin afectar la lógica de negocio, lo que la hace ideal para sistemas en evolución.
Antes de empezar Que es Vertical Slicing ?
Vertical Slicing es un enfoque en el desarrollo de software que consiste en dividir una aplicación en secciones más pequeñas y manejables, conocidas como "rebanadas" (slices), que abarcan todas las capas necesarias para completar una funcionalidad específica.
- Enfoque Integral: Cada rebanada representa una funcionalidad completa, que incluye el dominio, la lógica de negocio, la interfaz de usuario y cualquier interacción con la base de datos.
- Facilidad de Implementación: Permite a los equipos de desarrollo trabajar en funcionalidades específicas de manera independiente, facilitando la entrega continua y reduciendo el riesgo de integración.
- Mejor Comprensión: Al enfocarse en una única funcionalidad a la vez, se simplifica la comprensión y la gestión del código, haciendo que el proceso de desarrollo sea más ágil y menos propenso a errores.
En resumen, el Vertical Slicing promueve un desarrollo más estructurado y organizado, donde cada parte de la aplicación se construye y se entrega de forma coherente y alineada con los objetivos de negocio.
Screaming Architecture es un concepto en el diseño de software que se refiere a la idea de que la estructura y organización de tu código deben comunicar claramente las intenciones y propósitos del sistema. El término fue popularizado por el autor y desarrollador Robert C. Martin (también conocido como Uncle Bob).
Que es Screaming Architecture?
-
Claridad en la Intención: La arquitectura y el diseño del código deben reflejar claramente lo que hace la aplicación. Si alguien mira el código, debe "gritar" sobre su propósito y su funcionalidad, sin necesidad de leer comentarios extensos o documentación adicional.
-
Enfoque en el Dominio: La organización del código debe centrarse en el dominio de negocio, enfatizando las entidades, casos de uso y reglas que son relevantes para la lógica del negocio. Esto significa que el código está estructurado de tal manera que las partes más importantes (los elementos clave de negocio) son las más prominentes y accesibles.
-
Reducción de Complejidad: Al tener una estructura que "grita" su propósito, se reduce la complejidad y se mejora la mantenibilidad, ya que es más fácil para otros desarrolladores entender la lógica del sistema sin tener que desentrañar capas innecesarias o detalles de implementación.
Ejemplo en la Práctica:
Si un proyecto tiene una estructura de carpetas que refleja claramente sus funcionalidades clave (como "Pagos", "Usuarios", "Informes") en lugar de una estructura genérica o técnica (como "Capa de Presentación", "Capa de Negocio"), se considera que sigue los principios de Screaming Architecture.
En resumen, el objetivo de Screaming Architecture es crear un código que sea intuitivo y que claramente muestre el propósito y las capacidades de la aplicación, facilitando la comprensión y el trabajo en equipo.
Explicación de Cada Parte de la Arquitectura hexagonal
1. Dominio
El dominio contiene la lógica de negocio de la aplicación. Aquí se definen las entidades, los valores y las reglas que rigen el comportamiento del sistema.
2. Aplicación
La capa de aplicación gestiona las interacciones entre el dominio y los adaptadores. Aquí se encuentran los casos de uso que orquestan las operaciones en el dominio y las llamadas a los adaptadores.
3. Infraestructura
La infraestructura incluye las implementaciones concretas de los puertos, como repositorios, servicios externos, y conexiones a bases de datos. Aquí es donde se resuelven las dependencias concretas.
4. Presentación
La capa de presentación interactúa con los usuarios o sistemas externos. Puede ser una API REST, una aplicación web, una aplicación de escritorio, etc.
Estructura del Proyecto en Treeview
📂 src
├── 📂 InstitutoService
│ ├── 📂 Application
│ │ └── 📄 InstitutoService.cs
│ ├── 📂 Domain
│ │ ├── 📄 Instituto.cs
│ │ └── 📄 IInstitutoRepositorio.cs
│ ├── 📂 Infrastructure
│ │ ├── 📄 InstitutoRepositorioEF.cs
│ │ └── 📄 InstitutoDbContext.cs
│ ├── 📂 Presentation
│ │ └── 📄 InstitutoController.cs
│ └── 📄 InstitutoService.sln
Ejemplo de Código e Implementación por Archivo .cs
1. Instituto.cs
(Dominio)
public class Instituto
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Direccion { get; set; }
}
Descripción: Esta clase representa la entidad Instituto en el dominio, conteniendo las propiedades básicas que definen un instituto.
2. IInstitutoRepositorio.cs
(Dominio)
public interface IInstitutoRepositorio
{
Instituto ObtenerPorId(int id);
void Agregar(Instituto instituto);
void Actualizar(Instituto instituto);
void Eliminar(int id);
}
Descripción: Esta interfaz define las operaciones que se pueden realizar sobre la entidad Instituto, sirviendo como un puerto para el adaptador de infraestructura.
3. InstitutoRepositorioEF.cs
(Infraestructura)
public class InstitutoRepositorioEF : IInstitutoRepositorio
{
private readonly InstitutoDbContext _context;
public InstitutoRepositorioEF(InstitutoDbContext context)
{
_context = context;
}
public Instituto ObtenerPorId(int id)
{
return _context.Institutos.Find(id);
}
public void Agregar(Instituto instituto)
{
_context.Institutos.Add(instituto);
_context.SaveChanges();
}
public void Actualizar(Instituto instituto)
{
_context.Institutos.Update(instituto);
_context.SaveChanges();
}
public void Eliminar(int id)
{
var instituto = ObtenerPorId(id);
if (instituto != null)
{
_context.Institutos.Remove(instituto);
_context.SaveChanges();
}
}
}
Descripción: Esta clase implementa el repositorio usando Entity Framework, interactuando con la base de datos para realizar operaciones sobre la entidad Instituto.
4. InstitutoDbContext.cs
(Infraestructura)
public class InstitutoDbContext : DbContext
{
public DbSet<Instituto> Institutos { get; set; }
public InstitutoDbContext(DbContextOptions<InstitutoDbContext> options) : base(options) { }
}
Descripción: Este contexto de base de datos representa la sesión de trabajo con la base de datos y proporciona acceso a las entidades.
5. InstitutoService.cs
(Aplicación)
public class InstitutoService
{
private readonly IInstitutoRepositorio _repositorio;
public InstitutoService(IInstitutoRepositorio repositorio)
{
_repositorio = repositorio;
}
public Instituto ObtenerInstituto(int id)
{
return _repositorio.ObtenerPorId(id);
}
public void CrearInstituto(Instituto instituto)
{
_repositorio.Agregar(instituto);
}
public void ActualizarInstituto(Instituto instituto)
{
_repositorio.Actualizar(instituto);
}
public void EliminarInstituto(int id)
{
_repositorio.Eliminar(id);
}
}
Descripción: Este servicio coordina las operaciones relacionadas con los institutos, utilizando el repositorio para interactuar con la base de datos.
6. InstitutoController.cs
(Presentación)
[ApiController]
[Route("api/[controller]")]
public class InstitutoController : ControllerBase
{
private readonly InstitutoService _institutoService;
public InstitutoController(InstitutoService institutoService)
{
_institutoService = institutoService;
}
[HttpGet("{id}")]
public ActionResult<Instituto> GetInstituto(int id)
{
var instituto = _institutoService.ObtenerInstituto(id);
if (instituto == null)
{
return NotFound();
}
return instituto;
}
[HttpPost]
public IActionResult CreateInstituto([FromBody] Instituto instituto)
{
_institutoService.CrearInstituto(instituto);
return CreatedAtAction(nameof(GetInstituto), new { id = instituto.Id }, instituto);
}
[HttpPut("{id}")]
public IActionResult UpdateInstituto(int id, [FromBody] Instituto instituto)
{
if (id != instituto.Id)
{
return BadRequest();
}
_institutoService.ActualizarInstituto(instituto);
return NoContent();
}
[HttpDelete("{id}")]
public IActionResult DeleteInstituto(int id)
{
_institutoService.EliminarInstituto(id);
return NoContent();
}
}
Descripción: Este controlador maneja las peticiones HTTP relacionadas con los institutos, utilizando el servicio para ejecutar la lógica de negocio.
Conclusión
La arquitectura hexagonal proporciona un enfoque robusto para el desarrollo de aplicaciones, permitiendo una clara separación de responsabilidades. Esto no solo mejora la calidad del código, sino que también facilita la evolución y el mantenimiento de la aplicación. Al seguir esta arquitectura, se pueden implementar sistemas escalables y adaptables que respondan a las necesidades cambiantes del negocio.