Strategy + Factory: each courier is a strategy (Q18).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // ====================================================================== // Q: Show me Factory, Strategy and Adapter in a real scenario. // // Strategy + Factory: each courier is a strategy (Q18). The factory picks the // right one at runtime, here with keyed services (.NET 8). // Adapter: translates a foreign interface (the ERP SDK) into ours. This is also // the anti-corruption layer. // ====================================================================== // Strategy + Factory with keyed services (.NET 8) builder.Services.AddKeyedScoped<IShippingCalculator, DhlCalculator>("dhl"); builder.Services.AddKeyedScoped<IShippingCalculator, UpsCalculator>("ups"); builder.Services.AddScoped<IShippingCalculatorFactory, ShippingCalculatorFactory>(); public class ShippingCalculatorFactory(IServiceProvider sp) : IShippingCalculatorFactory { // IServiceProvider is allowed here: the factory is infrastructure, not business logic public IShippingCalculator Create(string carrier) => sp.GetKeyedService<IShippingCalculator>(carrier) ?? throw new NotSupportedException($"Carrier '{carrier}' is not supported"); } // Usage in an endpoint: [FromKeyedServices] when the key is known at compile time app.MapGet("/quote/dhl", ([FromKeyedServices("dhl")] IShippingCalculator calc) => /* ... */); // Adapter: ERP SDK behind our own interface (anti-corruption layer) public class SapErpAdapter(SapClient sap) : IErpClient { public Task SendOrderAsync(Order order, CancellationToken ct) { var sapOrder = new SapSalesOrder { KUNNR = order.CustomerId.ToString(), // SAP field names stay in here ITEMS = order.Lines.Select(l => new SapItem { MATNR = l.Sku, KWMENG = l.Quantity }).ToList() }; return sap.CreateSalesOrderAsync(sapOrder, ct); } } |