Closed ParsaMehdipour closed 4 months ago
Consider two services that are wrongly taking dependencies on each other. e.g., VoucherService
and DiscountService
.
Let's say in our example a method on VoucherService
called GenerateDiscountForVoucher
needs to call a method on the DiscountService.GenerateDiscountCode()
which in returns calls VoucherService.ValidateVoucher()
to validate the voucher status (again, this is an awful design). In this situation, your VoucherService
injects the DiscountService
using the conventional ctor injection mechanism. However the DiscountService
now cannot do the same (injecting the VoucherService
using ctor injection).
To have a workaround for this scenario, you can use the lazy initialization technique.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<VoucherService>();
//builder.Services.AddScoped<DiscountService>();
builder.Services.AddScoped<DiscountServiceWorkaround>();
var app = builder.Build();
app.MapGet("/", (VoucherService vs) =>
{
vs.Do(5);
return Results.Ok();
});
app.Run();
internal sealed class VoucherService(DiscountServiceWorkaround discountService, ILogger<VoucherService> logger)
{
public void Do(int x)
{
logger.LogInformation("voucher:do:before");
discountService.Do(x);
logger.LogInformation("voucher:do:after");
}
internal void Validate(int x)
{
logger.LogInformation("voucher:validate");
}
}
internal sealed class DiscountService(VoucherService voucherService, ILogger<DiscountService> logger)
{
public void Do(int x)
{
logger.LogInformation("discount:do:before");
voucherService.Validate(x);
logger.LogInformation("discount:do:after");
}
}
internal sealed class DiscountServiceWorkaround(IServiceProvider sp, ILogger<DiscountService> logger)
{
private VoucherService VoucherService => sp.GetRequiredService<VoucherService>();
public void Do(int x)
{
logger.LogInformation("discount_workaround:do:before");
VoucherService.Validate(x);
logger.LogInformation("discount_workaround:do:after");
}
}
Thank you for your informative answer. In this scenario, the two services are interdependent. Instead of one service calling the other directly, a workaround is implemented by using an intermediary dependent service. And uses the lazy initialization.
What alternative design would you recommend for this situation?
Well right off the bat these two come to mind:
VouherService
in this case, could enrich the object passed to the DiscountService
with an attribute like ValidityStatus
so that the DiscountService
doesn't need the method call altogether.Thanks I had mediator-like patterns as my solution of choice
In some cases, it might be necessary to create services after the constructor has been called. I would like to know about specific situations where this could occur.