0

I have inherited a project that has a ton of duplicate Repositories and duplicate Units of Work objects. For instance:

namespace MyProject.Domain.Plant.Repositories
{
    public interface IPlantEmployeeRepository : IRepository<Employee>
    {
    }
}
namespace MyProject.Domain.Plant.UnitOfWorks
{
    public interface IPlantUnitOfWork : Common.IUnitOfWork
    {
        public IPlantEmployeeRepository EmployeeRepository { get;  }
        public IPlantEmployeeFacilityRepository EmployeeFacilityRepository { get;  }
        // ...
    }
}
namespace MyProject.Infrastructure.Persistence.Plant.Repositories
{
    public class PlantEmployeeRepository : Repository<Employee>, IPlantEmployeeRepository
    {
        public PlantEmployeeRepository(PlantContext context) : base(context)
        {

        }
    }
}
namespace MyProject.Infrastructure.Persistence.Plant.UnitOfWorks
{
    public class PlantUnitOfWork : UnitOfWork, IPlantUnitOfWork
    {
        public IPlantEmployeeRepository EmployeeRepository => ServiceProvider.GetService<IPlantEmployeeRepository>();
        public IPlantEmployeeFacilityRepository EmployeeFacilityRepository => ServiceProvider.GetService<IPlantEmployeeFacilityRepository>();
        // ...

        public PlantUnitOfWork(PlantContext context, IServiceProvider serviceProvider) : base (context, serviceProvider) { }
    }
}
// ----------------------------------------------------------------------------------------------
namespace MyProject.Domain.Garage.Repositories
{
    public interface IGarageEmployeeRepository : IRepository<Employee>
    {
    }
}
namespace MyProject.Domain.Garage.UnitOfWorks
{
    public interface IGarageUnitOfWork : Common.IUnitOfWork
    {
        public IGarageEmployeeRepository EmployeeRepository { get;  }
        public IGarageEmployeeFacilityRepository EmployeeFacilityRepository { get;  }
        // ...
    }
}
namespace MyProject.Infrastructure.Persistence.Garage.Repositories
{
    public class GarageEmployeeRepository : Repository<Employee>, IGarageEmployeeRepository
    {
        public GarageEmployeeRepository(GarageContext context) : base(context)
        {

        }
    }
}
namespace MyProject.Infrastructure.Persistence.Garage.UnitOfWorks
{
    public class GarageUnitOfWork : UnitOfWork, IGarageUnitOfWork
    {
        public IGarageEmployeeRepository EmployeeRepository => ServiceProvider.GetService<IGarageEmployeeRepository>();
        public IGarageEmployeeFacilityRepository EmployeeFacilityRepository => ServiceProvider.GetService<IGarageEmployeeFacilityRepository>();
        // ...

        public GarageUnitOfWork(GarageContext context, IServiceProvider serviceProvider) : base (context, serviceProvider) { }
    }
}

Yes - there is no conceptual difference between these 2 representations of the DB. Both address the Employee, EmployeeFacility and other Employee related DB tables. There is no contexual boundaries, no root aggregates, no values and nothing else DDD related except the UoWs and Repositories.

Now, my plan was to create proxy objects that wrapped the UoW/Repositories. II am trying to write a single set of code that does the exact same thing on any collection of Employees, be it the Plant or the Garage or Corporate, etc.

namespace MyProject.Application.Engine.Zone.Proxies
{
    public abstract class BaseProxy : IZoneProxyUnitOfWork
    {
        public IUnitOfWork UnitOfWork { get; internal set; }
        public IRepository<IEmployee> RepositoryEmployee { get; internal set; }
        public IRepository<IEmployeeFacility> RepositoryEmployeeFacility { get; internal set; }
        // ...
    }


    public class PlantUnitOfWorkProxy : BaseProxy
    {
        public PlantUnitOfWorkProxy(IPlantUnitOfWork plantUnitOfWork)
        {
            UnitOfWork = plantUnitOfWork;
            RepositoryEmployee = (IRepository<IEmployee>)plantUnitOfWork.EmployeeRepository;
            RepositoryEmployeeFacility = (IRepository<IEmployeeFacility>)plantUnitOfWork.EmployeeFacilityRepository;
            // ...
       }
    }

    public class GarageUnitOfWorkProxy : BaseProxy
    {
        public GarageUnitOfWorkProxy(IGarageUnitOfWork garageUnitOfWork)
        {
            UnitOfWork = garageUnitOfWork;
            RepositoryEmployee = (IRepository<IEmployee>)garageUnitOfWork.EmployeeRepository;
            RepositoryEmployeeFacility = (IRepository<IEmployeeFacility>)garageUnitOfWork.EmployeeFacilityRepository;
            // ...
       }
    }
}

However, while all of this actually compiles the proxies will not initialize. Conversion from IPlantEmployeeRepository EmployeeRepository to IRepository<IEmployee> RepositoryEmployee is failing (which makes sense). However, my brain is broken enough now that fixing this is putting me in a tailspin: mainly trying to keep track of all the interfaces and how they are being used.

I will say this is a .NET Core solution utilizing C# though I don't think language is the really important here. Just humbly asking for some help.

Keith Barrows
  • 24,802
  • 26
  • 88
  • 134
  • So the reason for all this mess is the difference between how `PlantContext` & `GarageContext` are registered in DI? Why not try to merge the repository implementations by introducing `class EmployeeRepository : Repository where TContext : BaseEmployeeContext` ? Or generics for the interfaces `Proxy where TRepository : IRepository, ....` – Jeremy Lakeman Feb 18 '22 at 01:44
  • You first suggestion makes the most sense for this particular project. However, time constraints will not allow me to do that - and the lead dev is against wholesale "changes" as we don't have the crew to regress the entire mess and still meet delivery deadlines. You know - the usual. I am going to do a quick test with Generics to see if I can proxy everything I need quickly. To compound it every "slice" has its own dbContext and I have already found mismatches where some have old table defs. Handling that with extension methods for the OnModelCreating pieces. – Keith Barrows Feb 18 '22 at 03:31
  • Sounds like you'll be paying a significant tax on that technical debt. Perhaps more than a cleanup would take, but that's a hard call to make from here. – Jeremy Lakeman Feb 18 '22 at 04:40
  • SOLID was thrown out near the beginning (before I came on board). DRY is an unknown concept. Clean Architecture was dismissed in favor of DDD - yet DDD was *never* implemented. And this is a very heavy event driven app - with no event bus, no CQRS/ES, etc. Yes - the tech debt is extremely high and the team does not want to acknowledge the real debt we've incurred. But it is what it is and I'll manage to shoehorn in an elegant solution. – Keith Barrows Feb 18 '22 at 05:12

0 Answers0