I am looking at updating some of my companies existing code to allow unit tests to be implemented. To be able to do this, all repositories are being interfaced to allow DI. However, the existing code has a habit of creating 1 repository class per table in the database, meaning that one section of business logic may need to query 5 different repositories classes.
Passing in 5+ interfaces using DI seems messy and I am looking for help on how to redesign this.
This is all for an MVC 5 C# site, and an example of it is below (I have reduced the code size so that it is easier to read).
We have our controller for Account, which in this scenario manages the user sessions:
public class AccountController : Controller
{
private AccountLogic _accountLogic { get; set; }
public AccountController(
ISessionRepository sessionRepository,
ILoginRepository loginRepository)
{
_accountLogic = new AccountLogic(sessionRepository, loginRepository);
}
public IActionResult Index()
{
return View();
}
public IActionResult Login()
{
return View();
}
public IActionResult Login(LoginModel loginModel)
{
if (ModelState.IsValid)
{
if (_accountLogic.DoesUserExist(loginModel.Username))
{
var existingHash= _accountLogic.GetPassword(loginModel.Username);
if (Hash.VerifyPassword(loginModel.Password, existingHash))
{
// Set Auth Cookies
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError(
"Username",
"Sorry, we did not recognize " +
"either your username or password");
}
}
}
return View(loginModel);
}
}
The Team tries to reduce the size of the controller by having a business class perform most of the actions. What this actually means is that the business class is very large and has many responsibilities as it has to be passed an interface for all of the repositories it has to query. In the example shown, we pass in both the Session and Login repositories, each of this is solely responsible for their corresponding tables in the database.
public class AccountLogic
{
private ISessionRepository _sessionRepository { get; set; }
private ILoginRepository _loginRepository { get; set; }
public AccountLogic(
ISessionRepository sessionRepository,
ILoginRepository loginRepository)
{
_sessionRepository = sessionRepository;
_loginRepository = loginRepository;
}
public Guid CreateSession(int loginID, string userAgent, string locale)
{
return _sessionRepository.CreateSession(loginID, userAgent, locale);
}
public Session GetSession(Guid sessionKey)
{
return _sessionRepository.GetSession(sessionKey);
}
public void EndSession(Guid sessionKey)
{
_sessionRepository.EndSession(sessionKey);
}
public bool DoesUserExist(string username)
{
return _loginRepository.DoesUserExist(username);
}
public string GetPassword(string username)
{
return _loginRepository.GetPassword(username);
}
}
We then have the AccountLogic class, which handles all business logic. In the example given, there is no complicated logic to be performed, so it becomes purely a chain call to the interface methods.
My question is, how do we make this easier to handle will also being testable on a larger scale? If I had a controller which needs to query 10 different repositories, surely it is not manageable to pass 15 interfaces to the controller, and then to a relevant business class? Do we need to change how repositories are handled so that there is not one class per table?
AccountController
has anew
keyword in it. The most obvious refactoring here is to reduce the number of repositories, either by combining several entities into a single repository, or by using a Generic Repository.