C# – Eliminating cyclic dependency

cclass-designdependency-injectiondependency-managementdesign

I want to avoid cyclic dependency in this schema:

Diagram

DbRepository needs IUserDbEntityFactory to create User entity which needs IUserPublishedInfoInitializer to create UserPublishedInfo on request. And UserPublishedInfoInitializer itself needs IDbRepository to check whether the user is currently present in cache and active to set IsOnline property on UserPublishedInfo.

Both CreateUser and TryGetCachedUser methods use the same internal things like caching and locks inside DbRepository.

User is a an aggregation root db entity without interface.

DbRepository requires IUserDbEntityFactory which requires IUserPublishedInfoInitializer to construct User and makes cyclic dependency back to DbRepository. I usually use constructor dependency injection but in this case it won't work because of cyclic dependency so this is what the question is about.

Also I don't want to move MakePublishedInfo out of User because it's actually an implicit conversation in C# and is used very frequently in the code.

And passing IDbRepository to IUserPublishedInfoInitializer.Init as an argument is neither what I want because why should this implementation depend on what's using it? This breaks encapsulation.

On the schema only User and UserPublishedInfo are dynamically created objects (others are created at startup).

Should I change this design and what it should look like?

Best Answer

I would remove the Dependency of IUserPublishedInfoInitializer on IDbRepository. Instead inject this as a construction parameter into the concrete UserPublishedInfoInitializer.

your dependency graph then looks like

IUserPublishedInfoInitializer
    IUserDbEntityFactory 
    User
        IDbRepository
            DbRepository
            UserPublishedInfoInitializer

edit --

OK you just need a special constructor on DbRepository or child class

public DbRepository()
{
    var upii = new UserPublishedInfoInitializer(this);
    var this.userDbEntityFactory = new UserDbEntityFactory(upii);
}

if you still want to it via DI you could have a DbRepositoryFactory to do the same thing

public class DbRepositoryFactory : IDbRepositoryFactory 
{
    pulbic DbRepository Repo {get;set;}
}

public UserPublishedInfoInitializer(IDbRepositoryFactory repo)
{
    var this.DbRepositoryFactory= repo.Repo; //dont use this yet!!!
}

public Main()
{
    var dbfac = new DbRepositoryFactory()
    var upii = new UserPublishedInfoInitializer(dbfac);
    var repo = new DbRepository(upii)
    dbfac.Repo = repo;
}

However, I'm not sure injecting MORE things really helps with your core problem which is having the User object being uncreateable without a repository.

hard to say without seeing the whole problem but, just from the naming it seems like UserPublishedInfoInitializer is unnecessary, your repository could take responsibility for creating the UserPublishedInfo object if User is effectivly the aggregate root and always has a UserPublishedInfo property