using Books.Api.EventFlow.Extensions; using Books.Api.EventFlow.Infrastructure; using Books.Api.GraphQL; using Books.Api.Infrastructure; using Books.Api.Logging; using EventFlow; using EventFlow.Configuration; using EventFlow.Extensions; using EventFlow.Hangfire.Extensions; using EventFlow.PostgreSql.Connections; using EventFlow.PostgreSql.Extensions; using EventFlow.ReadStores; using EventFlow.Subscribers; using GraphQL; using Hangfire; using Hangfire.PostgreSql; using Npgsql; namespace Books.Api; public static class Startup { public static void ConfigureServices(IServiceCollection services, IConfiguration config, IHostEnvironment? environment = null) { var connectionString = config.GetConnectionString("Default") ?? throw new InvalidOperationException("Connection string 'Default' not found"); // Run database migrations (skipped in Test environment where migrations run separately with test connection string) var isTestEnvironment = environment?.EnvironmentName == "Test"; if (!isTestEnvironment) { DatabaseMigrator.Migrate(connectionString); } // PostgreSQL data source var dataSource = new NpgsqlDataSourceBuilder(connectionString).Build(); services.AddSingleton(dataSource); // Hangfire services.AddHangfire(c => c .SetDataCompatibilityLevel(CompatibilityLevel.Version_180) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() .UsePostgreSqlStorage(o => o.UseNpgsqlConnection(connectionString))); services.AddHangfireServer(); // Scheduler abstraction over Hangfire services.AddSingleton(); // EventFlow services.AddEventFlow(o => o .UsePostgreSqlEventStore() .ConfigurePostgreSql(PostgreSqlConfiguration.New.SetConnectionString(connectionString)) .AddDefaults(typeof(Startup).Assembly) .AddReadModels() .Configure(c => c.IsAsynchronousSubscribersEnabled = true) .UseHangfireJobScheduler()); // Resilience strategies services.AddSingleton(); services.AddSingleton(); // Read model repositories services.AddRepositories(); // Logging decorators services.DecorateAsyncEventHandlersWithLogging(); // GraphQL services.AddGraphQL(builder => builder .AddSchema() .AddSystemTextJson() .AddDataLoader() .AddGraphTypes(typeof(BooksSchema).Assembly) .AddErrorInfoProvider(opt => opt.ExposeExceptionDetails = true)); } }