$npx -y skills add wshaddix/dotnet-skills --skill dotnet-aot-architectureDesigning AOT-first apps. Source gen over reflection, AOT-safe DI, serialization, factories.
| 1 | # dotnet-aot-architecture |
| 2 | |
| 3 | AOT-first application design patterns for .NET 8+: preferring source generators over reflection, explicit DI registration over assembly scanning, AOT-safe serialization choices, library compatibility assessment, and factory patterns replacing `Activator.CreateInstance`. |
| 4 | |
| 5 | **Version assumptions:** .NET 8.0+ baseline. Patterns apply to all AOT-capable project types (console, ASP.NET Core Minimal APIs, worker services). |
| 6 | |
| 7 | **Out of scope:** Native AOT publish pipeline and MSBuild configuration -- see [skill:dotnet-native-aot]. Trim-safe library authoring and annotations -- see [skill:dotnet-trimming]. WASM AOT compilation -- see [skill:dotnet-aot-wasm]. MAUI-specific AOT -- see [skill:dotnet-maui-aot]. Source generator authoring (Roslyn API) -- see [skill:dotnet-csharp-source-generators]. DI container internals -- see [skill:dotnet-csharp-dependency-injection]. Serialization depth -- see [skill:dotnet-serialization]. |
| 8 | |
| 9 | Cross-references: [skill:dotnet-native-aot] for the AOT publish pipeline, [skill:dotnet-trimming] for trim annotations and library authoring, [skill:dotnet-serialization] for serialization patterns, [skill:dotnet-csharp-source-generators] for source gen mechanics, [skill:dotnet-csharp-dependency-injection] for DI fundamentals, [skill:dotnet-containers] for `runtime-deps` deployment, [skill:dotnet-native-interop] for general P/Invoke patterns and marshalling. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Source Generators Over Reflection |
| 14 | |
| 15 | The primary AOT enabler is replacing runtime reflection with compile-time source generation. Source generators produce code at build time that the AOT compiler can analyze and include. |
| 16 | |
| 17 | ### Key Source Generator Replacements |
| 18 | |
| 19 | | Reflection Pattern | Source Generator / AOT-Safe Alternative | Library | |
| 20 | |-------------------|---------------------------------------|---------| |
| 21 | | `JsonSerializer.Deserialize<T>()` | `[JsonSerializable]` context | System.Text.Json (built-in) | |
| 22 | | `Activator.CreateInstance<T>()` | Factory pattern with explicit `new` | Manual | |
| 23 | | `Type.GetProperties()` for mapping | `[Mapper]` attribute | Mapperly | |
| 24 | | `Regex` pattern compilation | `[GeneratedRegex]` attribute | Built-in (.NET 7+) | |
| 25 | | `ILogger.Log(...)` with string interpolation | `[LoggerMessage]` attribute | Microsoft.Extensions.Logging | |
| 26 | | Assembly scanning for DI | Explicit `services.Add*()` | Manual | |
| 27 | | `[DllImport]` P/Invoke | `[LibraryImport]` | Built-in (.NET 7+) | |
| 28 | | AutoMapper `CreateMap<>()` | `[Mapper]` source gen | Mapperly | |
| 29 | |
| 30 | ### Example: Migrating to Source Gen |
| 31 | |
| 32 | ```csharp |
| 33 | // BEFORE: Reflection-based (breaks under AOT) |
| 34 | var logger = loggerFactory.CreateLogger<OrderService>(); |
| 35 | logger.LogInformation("Order {OrderId} created for {Customer}", order.Id, order.CustomerId); |
| 36 | |
| 37 | // AFTER: Source-generated (AOT-safe, zero-alloc) |
| 38 | public partial class OrderService |
| 39 | { |
| 40 | [LoggerMessage(Level = LogLevel.Information, |
| 41 | Message = "Order {OrderId} created for {Customer}")] |
| 42 | private static partial void LogOrderCreated( |
| 43 | ILogger logger, int orderId, string customer); |
| 44 | } |
| 45 | |
| 46 | // Usage: |
| 47 | LogOrderCreated(_logger, order.Id, order.CustomerId); |
| 48 | ``` |
| 49 | |
| 50 | See [skill:dotnet-csharp-source-generators] for source generator mechanics and authoring patterns. |
| 51 | |
| 52 | --- |
| 53 | |
| 54 | ## AOT-Safe DI Patterns |
| 55 | |
| 56 | Dependency injection in AOT requires explicit service registration. Assembly scanning (`AddServicesFromAssembly`) and open-generic resolution may require reflection that AOT cannot satisfy. |
| 57 | |
| 58 | ### Explicit Registration (Preferred) |
| 59 | |
| 60 | ```csharp |
| 61 | var builder = WebApplication.CreateSlimBuilder(args); |
| 62 | |
| 63 | // Explicit registrations -- AOT-safe |
| 64 | builder.Services.AddSingleton<IOrderRepository, PostgresOrderRepository>(); |
| 65 | builder.Services.AddScoped<IOrderService, OrderService>(); |
| 66 | builder.Services.AddTransient<IEmailSender, SmtpEmailSender>(); |
| 67 | builder.Services.AddSingleton(TimeProvider.System); |
| 68 | ``` |
| 69 | |
| 70 | ### Avoid Assembly Scanning |
| 71 | |
| 72 | ```csharp |
| 73 | // BAD: Assembly scanning uses reflection -- breaks under AOT |
| 74 | builder.Services.Scan(scan => scan |
| 75 | .FromAssemblyOf<OrderService>() |
| 76 | .AddClasses(classes => classes.AssignableTo<IService>()) |
| 77 | .AsImplementedInterfaces() |
| 78 | .WithScopedLifetime()); |
| 79 | |
| 80 | // GOOD: Explicit registrations grouped by concern |
| 81 | builder.Services.AddOrderServices(); |
| 82 | builder.Services.AddInventoryServices(); |
| 83 | |
| 84 | // Extension method groups related registrations |
| 85 | public static class OrderServiceExtensions |
| 86 | { |
| 87 | public static IServiceCollection AddOrderServices( |
| 88 | this IServiceCollection services) |
| 89 | { |
| 90 | services.AddScoped<IOrderService, OrderService>(); |
| 91 | services.AddScoped<IOrderRepository, PostgresOrderRepository>(); |
| 92 | services.AddScoped<IOrderValidator, OrderValidator>(); |
| 93 | return services; |
| 94 | } |
| 95 | } |
| 96 | ``` |
| 97 | |
| 98 | ### Keyed Services (.NET 8+) |
| 99 | |
| 100 | ```csharp |
| 101 | // AOT-safe keyed service registration |
| 102 | builder.Services.AddKeyedSingleton<INotificationSender, EmailSender>("email"); |
| 103 | builder.Services.AddKe |