Hey devs, en este artículo te enseñaré una buena práctica, presente hoy en día en muchos proyectos reales, y como ya sabes que me encanta ser pragmático, aquí te lo enseñaré, se trata de crear un método de extensión para la inyección de dependencias o también algunos lo llaman módulo de inyección de dependencias.
La idea es centralizar la configuración de los servicios en una sola clase por capa o proyecto, promoviendo la separación de responsabilidades y manteniendo el archivo Program.cs más limpio y organizado.
Para lograr esto vamos a trabajar con un proyecto de prueba que usaremos con fines didácticos para poder aplicar, la que considero es la mejor forma de registrar servicios en el middleware cuando se trata de inyección de dependencias.
Tenemos un proyecto llamado RentManagement el cual tiene 5 proyectos:
- Api (hace las veces de Presentation)
- Application
- Contracts
- Domain
- Infrastructure
El proyecto RentManagement es de prueba, sólo para explicar este taller, no contiene funcionalidades, sólo lo mínimo necesario para mi explicación. Aquí esta el repositorio 🤝😃
La forma clásica
La forma normal y más común de registrar servicios en el middleware, es decir en la clase Program.cs es la siguiente.
En el proyecto de prueba tengo un sólo controlador llamado RentsController
[ApiController]
[Route("api/[controller]")]
public class RentsController : ControllerBase
{
private readonly IRentService subscriptionsService;
public RentsController(IRentService subscriptionsService)
{
this.subscriptionsService = subscriptionsService;
}
[HttpPost]
public IActionResult CreateSubscription(CreateRentRequest request)
{
var subscriptionId = subscriptionsService.CreateSubscription(
request.SubscriptionType.ToString(),
request.AdminId);
var response = new RentResponse(
subscriptionId,
request.SubscriptionType);
return Ok(response);
}
}
Como notarás, he inyectado IRentService que viene del proyecto RentManagement.Application, así que ahora tocará registrar esto en Program.cs y la forma clásica de hacer esto es así:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped<IRentService, RentService>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Sólo como dato adicional, si no registras correctamente las inyecciones de dependencias, obtendrás un error como "Unable to resolve service for type ..."
El mejor método y el porqué
A continuación te explicaré cuál considero yo que es la mejor forma de hacer esto: Crearemos una clase llamada DependencyInjection en cada capa, en este caso Application, y de esta manera cada capa gestionará la lógica y la configuración de sus dependencias cuando son inyectadas. Lo que tendrá el middleware, es decir la clase Program.cs simplemente será una llamada.
De esta forma estamos haciendo que la capa Presentation (Api) sea responsable del registro (mediante una sóla línea de código) y ya cada capa sería responsable de los detalles de dicho registro y manejarán su propia lógica. Esta es una forma de aplicar el principio SOLID Single Responsibility y además promover como ya he mencionado, la separación de responsabilidades o preocupaciones.
Además considerando que en Clean Architecture cada capa debe estar desacoplada al máximo, esto viene muy bien y por eso es una buena práctica.
Pasemos a implementar esto:
Instalar Microsoft.Extensions.DependencyInjection.Abstractions en el proyecto que contiene la interface e implementación concreta, es decir en este caso sería el proyecto que contiene IRentService y RentService.
El proyecto es RentManagement.Application
Después deberás crear la clase DependencyInjection
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddScoped<IRentService, RentService>();
return services;
}
}
Ahora actualiza Program.cs del proyecto RentManagement.Api
using RentManagement.Application.Services;
using RentManagement.Application;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
//builder.Services.AddScoped<IRentService, RentService>(); nos deshacemos de esta forma
builder.Services.AddApplication();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Esta misma práctica podrías aplicar para las demás capas como Infrastructure o la que necesites configurar.
Ahora si pruebas el api, notarás que funciona de igual manera, es decir se están registrando correctamente las inyecciones de dependencias configuradas en el middleware, y con buenas prácticas.
Ahora sí, en la capa de Presentation o Api como lo llame en este proyecto de prueba, sólo tendrás una elegante línea de código en el middleware por cada capa.
Si esta entrada te ha gustado, compártela crack! 😃🐿️
Créditos de imagen de portada: Foto de ThisisEngineering en Unsplash