Reading configuration by key from a dictionary means every consumer knows the key names and nothing validates them; binding to a class means the shape is declared once.
public sealed class StripeOptions
{
[Required] public string ApiKey { get; set; }
[Range(1, 30)] public int TimeoutSeconds { get; set; } = 3;
}
services.AddOptions<StripeOptions>()
.Bind(Configuration.GetSection("Stripe"))
.ValidateDataAnnotations();
// injected as IOptions<StripeOptions>
Validation at startup is the property that matters: a missing key is a failure to boot rather than a null appearing during a request three hours later. IOptionsSnapshot re-reads per request and IOptionsMonitor notifies on change, which is the reloading behaviour PHP has no equivalent of at all. The pattern transfers directly — a typed configuration object validated at boot is worth building in any language.