Hey devs, en esta oportunidad les vengo a hablar del Options Pattern, el cual es un patrón de configuración específico de .NET que aprovecha el sistema de inyección de dependencias para gestionar la configuración de manera modular y escalable.
Es un enfoque de diseño en .NET para gestionar la configuración de una aplicación de manera estructurada y además flexible. Permite encapsular los parámetros de configuración en clases fuertemente tipadas y proporciona un acceso sencillo y organizado a los valores de configuración, lo que facilita el mantenimiento y la reutilización del código.
Este patrón, no es un patrón de diseño, ¿por qué? Los patrones de diseño clásicos, como los del libro "Design Patterns: Elements of Reusable Object-Oriented Software" son soluciones generales para problemas de diseño de software orientado a objetos, aplicables a diversos lenguajes y plataformas. En contraste, el Options Pattern es un patrón específico de .NET para gestionar configuraciones de aplicaciones de manera estructurada y flexible dentro de ASP.NET Core, utilizando la infraestructura de configuración e inyección de dependencias de .NET.
Características
Clases fuertemente tipadas: En vez de acceder a las configuraciones con claves de cadena, el Options Pattern usa clases con propiedades específicas, lo que hace el código más seguro y fácil de mantener.
Vinculación automática: El Options Pattern enlaza automáticamente los valores de configuración (como en appsettings.json) a las clases, facilitando su uso a través de dependencias inyectadas.
Inyección de dependencias: Las clases de opciones se inyectan en servicios o controladores, evitando la lectura manual de archivos de configuración y permitiendo acceder fácilmente a los valores necesarios.
Cómo aplicarlo...
Vamos a suponer que tenemos un API y hemos configurado su appsettings.Development.json con datos de emails:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"EmailSettings": {
"Port": "123",
"Server": "myserver.net"
}
}
Ahora tenemos que definir una clase que contenga los valores de las configuraciones presentes en el appsettings, entonces creamos la siguiente clase:
namespace TestApi
{
public class AppSettings
{
public EmailSettings EmailSettings { get; set; } = default!;
}
public class EmailSettings
{
public int Port { get; set; }
public string Server { get; set; } = default!;
}
}
Ahora toca registrar la clase en Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<AppSettings>(builder.Configuration);
// Add services to the container.
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();
Entonces utilizamos los valores de configuración en un controlador (o en una clase de servicio, como lo prefieras) para eso inyectamos un IOptions genérico indicándole nuestra clase contenedora de las configuraciones:
[ApiController]
[Route("/api/[controller]")]
public class TestController : ControllerBase
{
private readonly IOptions<AppSettings> options;
public TestController(IOptions<AppSettings> options)
{
this.options = options;
}
[HttpGet]
public IActionResult Get()
{
var emailSettings = options.Value.EmailSettings;
return Ok($"El servidor es {emailSettings.Server} y el puerto es {emailSettings.Port}");
}
}
Finalmente probamos el API:
Como notarás, estamos trayendo valores del appsettings hacia otras capas, en este caso directamente a un controlador pero podrías llevar esa información a cualquier clase en cualquier capa según la arquitectura que estés utilizando y mediante inyeccion de dependencias hacerlo de una forma segura y con mejores prácticas.
Tipos de 'Options'
Aquí hemos inyectado IOptions<> pero son 3 tipos los que podemos inyectar, te los explico:
IOptions<T>: Proporciona acceso a una instancia de opciones de solo lectura, lo que quiere decir que el sistema lo registra como singleton.
IOptionsSnapshot<T>: Proporciona acceso a opciones que se pueden recargar en tiempo de ejecución, es decir que se usa cuando tus valores de configuración del appsettings van a estar cambiando de valor durante la ejecución de tu aplicación o API, es registrado como Scoped.
IOptionsMonitor<T>: Proporciona acceso a las opciones que admiten notificaciones de cambios y pueden recargarse automáticamente en tiempo real, es registrado como Singleton pero puede leerse en cualquier momento, es decir que si cambias los valores de tu appsettings con tu API en ejecución, estos nuevos valores serán leídos.
Si esta entrada te ha gustado dev, entonces compártela con todo tu equipo de ingeniería 🙌
Créditos de imagen de portada: DALL-E 3 (Ojo que la IA se equivocó en la imagen, checa el monitor de atrás xd)