user3141326
user3141326

Reputation: 1533

Simplifying composition interfaces in C#

In the code I am working on I have a structure where some portions of the code depend on the current software session. Software session contains multiple helper objects which are dependency injected by composition.

One example is IRepository injected to it, which contains access to the data repository. And the IRepository contains a DatabaseContext which writes to a database, via IDbContext again which is injected.

SoftwareSession is the only injected common infrastructure for accessing all the way to the database, acting as a gateway. This means when I want to write an object to database, for instance WriteCar I will have to implement 3 interfaces, 2 functions delegating to composed objects and 1 function with implementation. It is clarified in the code fragment below. The WriteCar signatures are defined the same in 3 interfaces (IRepository, ISoftwareSession, IDbContext), 2 places where it is not implemented (Repository, SoftwareSession) which simply calls composited objects related functions and 1 place of actual implementation (IDbContext)

This means when I want to refactor, move code, add functionality or change function signatures I will always have to change 6 places for one function.

I think this provides the best environment for improving testability and it follows best practices where software session wraps access to repository and repository wraps access to data contexts - yet I still am questioning if we can have some better way of writing it once, or do I have a misunderstanding of some concept in the code below?

What is the architecturally more maintainable way of implementing this? Maybe even using some clever way of lambdas or delegates to reduce the amount of code written for each new functionality? Or even some libraries (like automapper simplifies DTOs) or tools to ease generation of this code from some kind of templating mechanism using Visual Studio, Resharper, etc?

Please let me know if I am having some confusion of concepts here. I know some my colleagues have similar views, in which case it may be helpful to clarify misunderstandings of others as well.

public class SoftwareSession : ISoftwareSession
{
    ...
    IRepository repository;
    public void WriteCar(Car car){
        repository.WriteCar(car);
    }
    ...
}

public interface ISoftwareSession{
    ...
    void WriteCar(Car car);
    ...
}


public class Repository : IRepository{
    ...
    IDbContext context;
    public void WriteCar(Car car){
        context.WriteCar(car);
    }
    ...        
}

public interface IRepository{
    ...
    void WriteCar(Car car);
    ...
}

public class MyDbContext : IDbContext{
    ...
    public void WriteCar(Car car){
       //The Actual Implementation here.
        ...
    }
    ...
}

public interface IDbContext{
    ...
    void WriteCar(Car car);
    ...
}

Upvotes: 2

Views: 1673

Answers (2)

rory.ap
rory.ap

Reputation: 35310

Without seeing your composition root, I'm not entirely sure how your implementation works, but I'd suggest looking into using an Inversion of Control (IoC) container. Since your ISoftwareSession implementation only depends on an IRepository instance, you only need to inject that in the class' constructor. The same goes for your IRepository implementation: you only need to inject your IDbContext into the constructor.

With the IoC container, you "register", i.e. wire up your interfaces to your implementation at application startup (in the composition root), and the container takes care of creating the required instances when you resolve the dependencies. Then all you have to do is get the instance of SoftwareSession from the container, and away you go.

So, you could change your SoftwareSession implementation like this:

public class SoftwareSession : ISoftwareSession
{
    IRepository repository;

    public SoftwareSession(IRepository repository)
    {
        this.repository = repository;
    }

    public void WriteCar(Car car)
    {
        repository.WriteCar(car);
    }
}

And your Repository implementation like this:

public class Repository : IRepository
{
    IDbContext context;

    public Repository(IDbContext dbContext)
    {
        context = dbContext;
    }

    public void WriteCar(Car car)
    {
        context.WriteCar(car);
    }
}

Then here is your composition root:

var ioc = new MyIocContainer();

// register your interfaces and their associated implementation types with the IoC container
ioc.Register<ISoftwareSession, SoftwareSession>();
ioc.Register<IRepository, Repository>();
ioc.Register<IDbContext, MyDbContext>();

// resolve the IoC container
ioc.Resolve();

// get your `ISoftwareSession` instance
var session = ioc.GetConcrete<ISoftwareSession>();

var newCar = new Car();

session.WriteCar(newCar);

Upvotes: 1

Zoran Horvat
Zoran Horvat

Reputation: 11301

For one thing, your IDbContext and IRepository are the same. You would probably like to remove IDbContext, or at least to remove methods declared in IRepository from it.

Then, both MyDbContext and Repository would implement IRepository and Repository class would just be a wrapper around MyDbContext.

Then, if Repository is only forwarding calls to MyDbContext, then you probably don't need that class either.

Furthermore, I don't see that you are doing anything in the SoftwareSession apart from forwarding the call to the contained repository. Do you really need SoftwareSession, or would it make sense to pass IRepository directly to whoever is calling the session object?

Bottom line is that this implementation is swarming with duplication and forwarding. Remove that, and your entire model would become simple.

Upvotes: 1

Related Questions