I have a IItemProcessor class which takes each item from a list and send it to a web API (IApiService). The processing is done asynchronously, on another thread.
If web API responds with 'user not authorized', app needs to logout. For this, I have an IApp which has LogoutUser() method.
Currently, the ItemProcessor implementation catches errors thrown by IApiService and if the error is 'not authorized', it calls IApp:LogoutUser.
The issue is in the implementation of IApp:LogoutUser I need to call IItemProcessor:Stop to stop processing. Which means there's a circular dependency between App and `ItemProcessor.
I'm using Dependency Injection in the c-tor and this gives me infinite recursive construction of the concrete objects:
public class App : IApp
{
public App(IItemProcessor itemProcessor)
{
...
}
public void Logout()
{
_itemProcessor.Stop();
}
}
public class ItemProcessor : IItemProcessor
{
public ItemProcessor (IApp app)
{
...
}
public void Stop() {
...
}
void Run()
{
... for each item in list ..
try
{
... try send item ....
}
catch(UserNotAuthorizedException)
{
_app.Logout();
}
}
}
How can I fix this?
IApp needs to know about IItemProcessor to start and stop it on user login and logut. But I feel like IItemProcessor breaks SRP, it shouldn't actually call Logout, but rather somehow inform "someone" about the error, and that "someone" has in turn the responsability to call IApp:LogoutUser. But what should this "someone" be? Should it be a listener to the item processor errors?
I'm having a hard time figuring out how to split responsibilities.
I tried to use command pattern by having a LogoutCommand
public class LogoutCommand
{
readonly IApp _app;
public LogoutCommand(IApp app)
{
_app = app;
}
public void Execute()
{
_app.UserLogout();
}
}
the ItemProcessor implementation could call logoutCommand.Execute().
This would decouple ItemProcessor from knowing about App, but I have the same issue because when App c-tor calls ItemProcessor c-tor which calls LogoutCommand which calls App c-tor.
Another (bad) idea: I thought a factory could alleviate this issue. I thought having a ILogoutCommandFactory factory which creates the LogoutCommand. As long as I didn't create the LogoutCommand in the c-tor of ItemProcessor, I thought it could work. But the fact that I can't do that, feels to me like code smell.
To me DI in c-tor seems to start looking like an issue.