Files
the-biergarten-app/web/backend/API/API.Core/Program.cs
Aaron Po 5cf4df1fd8 Migrate UserManagement to a vertical slice (Features.UserManagement)
Same pattern as the Breweries migration: Service.UserManagement and the
UserAccount repository fold into Features.UserManagement, with MediatR
queries replacing UserService. UpdateUserCommand/Handler carry forward
IUserService.UpdateAsync as-is (it has no HTTP route today, and adding one
is a separate decision from this migration). Adds handler/repository test
coverage that didn't exist before, since Service.UserManagement had no
test project.
2026-06-20 15:54:53 -04:00

120 lines
3.8 KiB
C#

using API.Core;
using API.Core.Authentication;
using Features.Breweries.Controllers;
using Features.Breweries.DependencyInjection;
using Features.UserManagement.Controllers;
using Features.UserManagement.DependencyInjection;
using FluentValidation;
using FluentValidation.AspNetCore;
using Infrastructure.Email;
using Infrastructure.Email.Templates.Rendering;
using Infrastructure.Jwt;
using Infrastructure.PasswordHashing;
using Infrastructure.Repository.Auth;
using Infrastructure.Sql;
using Service.Auth;
using Service.Emails;
using Shared.Application.Behaviors;
var builder = WebApplication.CreateBuilder(args);
// Global Exception Filter
builder.Services.AddControllers(options =>
{
options.Filters.Add<GlobalExceptionFilter>();
})
.AddApplicationPart(typeof(BreweryController).Assembly)
.AddApplicationPart(typeof(UserController).Assembly);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddOpenApi();
// Add FluentValidation
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.Services.AddValidatorsFromAssembly(typeof(BreweryController).Assembly);
builder.Services.AddValidatorsFromAssembly(typeof(UserController).Assembly);
builder.Services.AddFluentValidationAutoValidation();
// Add MediatR. Each Features.* slice's assembly is registered here as it's introduced;
// ValidationBehavior runs FluentValidation validators in the MediatR pipeline for command/query handlers.
builder.Services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblyContaining<Program>();
cfg.RegisterServicesFromAssembly(typeof(BreweryController).Assembly);
cfg.RegisterServicesFromAssembly(typeof(UserController).Assembly);
cfg.AddOpenBehavior(typeof(ValidationBehavior<,>));
});
// Add health checks
builder.Services.AddHealthChecks();
// Configure logging for container output
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
if (!builder.Environment.IsProduction())
{
builder.Logging.AddDebug();
}
// Configure Dependency Injection -------------------------------------------------------------------------------------
builder.Services.AddSingleton<
ISqlConnectionFactory,
DefaultSqlConnectionFactory
>();
builder.Services.AddScoped<IAuthRepository, AuthRepository>();
builder.Services.AddFeaturesBreweries();
builder.Services.AddFeaturesUserManagement();
builder.Services.AddScoped<ILoginService, LoginService>();
builder.Services.AddScoped<IRegisterService, RegisterService>();
builder.Services.AddScoped<ITokenService, TokenService>();
builder.Services.AddScoped<ITokenInfrastructure, JwtInfrastructure>();
builder.Services.AddScoped<IPasswordInfrastructure, Argon2Infrastructure>();
builder.Services.AddScoped<IEmailProvider, SmtpEmailProvider>();
builder.Services.AddScoped<IEmailTemplateProvider, EmailTemplateProvider>();
builder.Services.AddScoped<IEmailService, EmailService>();
builder.Services.AddScoped<IConfirmationService, ConfirmationService>();
// Register the exception filter
builder.Services.AddScoped<GlobalExceptionFilter>();
// Configure JWT Authentication
builder
.Services.AddAuthentication("JWT")
.AddScheme<JwtAuthenticationOptions, JwtAuthenticationHandler>(
"JWT",
options => { }
);
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.MapOpenApi();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
// Health check endpoint (used by Docker health checks and orchestrators)
app.MapHealthChecks("/health");
app.MapControllers();
app.MapFallbackToController("Handle404", "NotFound");
// Graceful shutdown handling
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() =>
{
app.Logger.LogInformation("Application is shutting down gracefully...");
});
app.Run();