# Solution Foundation Design ## Overview This document describes the infrastructure architecture for the .NET 10 solution, including project structure, dependency injection patterns, and configuration management. ## Project Structure ``` NEW/src/ ├── JdeScoping.Core/ # Domain models, interfaces, shared logic │ ├── Models/ # Entity classes (Search, WorkOrder, Lot, etc.) │ ├── Interfaces/ # Service contracts (ISearchRepository, etc.) │ ├── Options/ # Configuration binding classes │ └── Extensions/ # Service registration extension methods ├── JdeScoping.Host/ # ASP.NET Core host (Web API + BackgroundServices) │ ├── Program.cs # Application entry point, DI configuration │ ├── appsettings.json # Production configuration │ ├── appsettings.Development.json # Development overrides │ └── Controllers/ # API endpoints ├── JdeScoping.Client/ # Blazor WebAssembly UI │ └── (deferred to UI phase) └── JdeScoping.Database/ # DbUp migrations (already exists) ├── Scripts/ # Migration SQL files └── DatabaseMigrator.cs # DbUp configuration ``` ## DI Registration Pattern ### Extension Method Convention Each module provides an extension method on `IServiceCollection`: ```csharp public static class DataAccessServiceExtensions { public static IServiceCollection AddDataAccess( this IServiceCollection services, IConfiguration configuration) { // Bind configuration services.Configure( configuration.GetSection(DataAccessOptions.SectionName)); // Register services services.AddScoped(); services.AddScoped(); services.AddScoped(); return services; } } ``` ### Module Registration Order Extensions are called in dependency order in Program.cs: ```csharp builder.Services .AddDataAccess(builder.Configuration) // 1. Database access .AddDataSync(builder.Configuration) // 2. Cache synchronization .AddSearchProcessing(builder.Configuration) // 3. Search execution .AddExcelExport(builder.Configuration) // 4. Result export .AddAuth(builder.Configuration); // 5. Authentication ``` ### Lifetime Guidelines | Service Type | Lifetime | Rationale | |--------------|----------|-----------| | Repository | Scoped | Database connection per request | | DbContext (if used) | Scoped | EF Core default | | Options classes | Singleton | Cached configuration | | HttpClient | Singleton | Connection pooling | | BackgroundService | Singleton | Long-running workers | | Processors | Transient | Stateless operations | ## Configuration Sections ### appsettings.json Structure ```json { "ConnectionStrings": { "LotFinder": "Server=...;Database=LotFinder;...", "JDE": "Data Source=...;User ID=...;Password=...", "CMS": "Data Source=...;Port=...;Database=..." }, "DataAccess": { "CommandTimeoutSeconds": 120, "EnableDetailedLogging": false }, "DataSync": { "MassRefreshCronSchedule": "0 0 6 * * SAT", "DailyRefreshCronSchedule": "0 0 4 * * *", "HourlyRefreshCronSchedule": "0 0 * * * *", "MaxConcurrentUpdates": 4 }, "Auth": { "LdapUrl": "LDAP://directory.company.com", "LdapGroup": "CN=LotFinderUsers,OU=Groups,DC=company,DC=com", "CookieExpirationMinutes": 480 }, "ExcelExport": { "TempDirectory": "/tmp/lotfinder", "MaxRowsPerSheet": 1048576, "DefaultDateFormat": "yyyy-MM-dd HH:mm:ss" }, "SearchProcessing": { "PollingIntervalSeconds": 5, "MaxConcurrentSearches": 2, "SearchTimeoutMinutes": 30 } } ``` ### appsettings.Development.json Overrides ```json { "ConnectionStrings": { "LotFinder": "Server=localhost,1434;Database=LotFinder;User Id=scopingapp;Password=...;TrustServerCertificate=True" }, "DataAccess": { "EnableDetailedLogging": true }, "DataSync": { "MassRefreshCronSchedule": "", "DailyRefreshCronSchedule": "", "HourlyRefreshCronSchedule": "" }, "Logging": { "LogLevel": { "Default": "Debug", "Microsoft.AspNetCore": "Warning" } } } ``` ## Options Classes ### Naming Convention - Class name: `{Module}Options` - Section name: Same as class name without "Options" suffix - Static constant: `SectionName` for configuration binding ### DataAccessOptions ```csharp public class DataAccessOptions { public const string SectionName = "DataAccess"; public int CommandTimeoutSeconds { get; set; } = 120; public bool EnableDetailedLogging { get; set; } = false; } ``` ### DataSyncOptions ```csharp public class DataSyncOptions { public const string SectionName = "DataSync"; public string MassRefreshCronSchedule { get; set; } = "0 0 6 * * SAT"; public string DailyRefreshCronSchedule { get; set; } = "0 0 4 * * *"; public string HourlyRefreshCronSchedule { get; set; } = "0 0 * * * *"; public int MaxConcurrentUpdates { get; set; } = 4; } ``` ### AuthOptions ```csharp public class AuthOptions { public const string SectionName = "Auth"; public string LdapUrl { get; set; } = string.Empty; public string LdapGroup { get; set; } = string.Empty; public int CookieExpirationMinutes { get; set; } = 480; } ``` ### ExcelExportOptions ```csharp public class ExcelExportOptions { public const string SectionName = "ExcelExport"; public string TempDirectory { get; set; } = "/tmp/lotfinder"; public int MaxRowsPerSheet { get; set; } = 1048576; public string DefaultDateFormat { get; set; } = "yyyy-MM-dd HH:mm:ss"; } ``` ### SearchProcessingOptions ```csharp public class SearchProcessingOptions { public const string SectionName = "SearchProcessing"; public int PollingIntervalSeconds { get; set; } = 5; public int MaxConcurrentSearches { get; set; } = 2; public int SearchTimeoutMinutes { get; set; } = 30; } ``` ## NuGet Package Dependencies ### JdeScoping.Core ```xml ``` ### JdeScoping.Host ```xml ``` ## Program.cs Structure ```csharp var builder = WebApplication.CreateBuilder(args); // Database migrations DatabaseMigrator.Migrate(builder.Configuration.GetConnectionString("LotFinder")!); // Module registration builder.Services .AddDataAccess(builder.Configuration) .AddDataSync(builder.Configuration) .AddSearchProcessing(builder.Configuration) .AddExcelExport(builder.Configuration) .AddAuth(builder.Configuration); // ASP.NET Core services builder.Services.AddControllers(); builder.Services.AddSignalR(); var app = builder.Build(); // Middleware pipeline app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapHub("/statushub"); app.Run(); ``` ## Validation Approach ### Startup Validation Validate critical services are registered at startup: ```csharp // In Program.cs after building using var scope = app.Services.CreateScope(); _ = scope.ServiceProvider.GetRequiredService(); _ = scope.ServiceProvider.GetRequiredService>(); // ... validate other critical services ``` ### Configuration Validation Use DataAnnotations or IValidateOptions for configuration: ```csharp public class DataAccessOptionsValidator : IValidateOptions { public ValidateOptionsResult Validate(string? name, DataAccessOptions options) { if (options.CommandTimeoutSeconds <= 0) return ValidateOptionsResult.Fail("CommandTimeoutSeconds must be positive"); return ValidateOptionsResult.Success; } } ```