Reputation: 55
In case of Clean/Onion Architecture what would be the best solution for services in Application layer with bloated dependencies? For example if I have service with dependencies that I don't use in every method.
Like here:
public class UserService : IUserService
{
private readonly IService1 _service1;
private readonly IService2 _service2;
private readonly IService3 _service3;
private readonly IService4 _service4;
private readonly IService5 _service5;
public UserService(
IService1 service1,
IService2 service2,
IService3 service3,
IService4 service4,
IService5 service5)
{
_service1 = service1;
_service2 = service2;
_service3 = service3;
_service4 = service4;
_service5 = service5;
}
public async Task<UserDto> GetUserAsync(int id)
{
_service3.Log($"Fetching user {id}");
var user = await _service1.GetUserByIdAsync(id);
return _service2.MapToDto(user);
}
public async Task<bool> AuthenticateAsync(string username, string password)
{
_service3.Log($"Authenticating {username}");
var user = await _service1.GetUserByUsernameAsync(username);
return await _service4.VerifyPasswordAsync(user, password);
}
}
In preceding example we have two methods where each use specific services when we call them but at the same redundant services are being injected.
I was wondering if I should use different architecture or am I doing things incorrectly
Upvotes: 0
Views: 56
Reputation: 10225
Use the Lazy Load pattern (https://en.wikipedia.org/wiki/Lazy_loading):
Lazy loading (also known as asynchronous loading) is a technique used in computer programming, especially web design and web development, to defer initialization of an object until it is needed.
For example:
private IService5 _Service5 = null;
public IService5 Service5
{
get
{
if(_Service5 == null)
{
// Do something expensive
_Service5 = new SomeService()
}
return _Service5;
}
}
You can use references to Service5 as much as you like, the 'expensive' work to instantiate Service5 won't happen at all unless it's called e.g. Service5.DoThing()
. You'll get a performance hit on that first call, but not thereafter.
It also means that you won't get a big performance hit when you call UserService()
as long as UserService()
doesn't invoke instantiation of all the services.
In your code you are passing in references to all the services you want to use, that's not ideal. Instead:
Update
But won't that break DI pattern if I'm using
_Service5 = new SomeService()
? Maybe I should use Service Locator Pattern?
Yes you can, but the service factory will still need to know which service and which service implementation to instantiate, my suggestion (#3 above) is to pass the necessary parameters in through your constructor.
What form those service parameters take will depend - you might get away with a simple string, or you might even have a simple class where you can more explicitly define parameters.
public UserService(
string service1Key, // e.g. "Cosmos_ThingyDB_UAT"
serviceParamsDTO service2ParameterDTO // e.g.
// ServiceType = UserIdentityService;
// UserID = "RFeilding";
)
{
_service1Key = service1Key;
_service2Parameter = service2ParameterDTO;
}
private IService1 _Service1 = null;
public IService1 Service1
{
get
{
if(_Service1 == null)
{
// Do something expensive
_Service1 = ServiceFactory(_service1Key);
}
return _Service1;
}
}
In either case, the actual values you're passing in could be:
The issue with #1 is that it will get messy and be a maintenance nightmare, especially when you get a new service implementation that needs an additional parameter. Using a simple class (e.g. serviceParamsDTO
) to pass the parameters would help with that.
Upvotes: 0