Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Architecture problem: use of dependency injection resulting in rubbish API

I'm tring to create a class which does all sorts of low-level database-related actions but presents a really simple interface to the UI layer.

This class represents a bunch of data all within a particular aggregate root, retrieved by a single ID int.

The constructor takes four parameters:

public AssetRegister(int caseNumber, ILawbaseAssetRepository lawbaseAssetRepository, IAssetChecklistKctcPartRepository assetChecklistKctcPartRepository, User user)
{
  _caseNumber = caseNumber;
  _lawbaseAssetRepository = lawbaseAssetRepository;
  _assetChecklistKctcPartRepository = assetChecklistKctcPartRepository;
  _user = user;
  LoadChecklists();
}

The UI layer accesses this class through the interface IAssetRegister. Castle Windsor can supply the ILawbaseAssetRepository and IAssetChecklistKctcPartRepository parameters itself, but the UI code needs to supply the other two using an anonymous type like this:

int caseNumber = 1000;
User user = GetUserFromPage();
IAssetRegister assetRegister = Moose.Application.WindsorContainer.Resolve<IAssetRegister>(new { caseNumber, user});

From the API design point of view, this is rubbish. The UI layer developer has no way of knowing that the IAssetRegister requires an integer and a User. They need to know about the implementation of the class in order to use it.

I know I must have some kind of design issue here. Can anyone give me some pointers?

like image 505
David Avatar asked Mar 11 '11 11:03

David


People also ask

What is the problem with dependency injection?

One problem with constructor injection is that if your class has many dependencies, then your constructor signature gets unwieldy and hard to read. Now, when you have multiple dependencies that you need to inject, each one is a separate set method and so we don't have a long list of constructor parameters.

What is the disadvantage of dependency injection?

The main drawback of dependency injection is that using many instances together can become a very difficult if there are too many instances and many dependencies that need to be resolved.

Is dependency injection really needed?

The dependency injection technique enables you to improve this even further. It provides a way to separate the creation of an object from its usage. By doing that, you can replace a dependency without changing any code and it also reduces the boilerplate code in your business logic.

What is the limitation of constructor injection?

The main disadvantage to Constructor Injection is that if the class you're building is called by your current application framework, you might need to customize that framework to support it. Some frameworks assume that your classes will have a parameterless constructor.


1 Answers

Try separating the message from the behavior. Make a class that holds the data for the operation, and create a different class that contains the business logic for that operation. For instance, create this command:

public class RegisterAssetCommand
{
    [Required]
    public int CaseNumber { get; set; }

    [Required]
    public User Operator { get; set; }
}

Now define an interface for handling business commands:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

Your presentation code will now look like this:

var command = new RegisterAssetCommand
{
    CaseNumber = 1000,
    Operator = GetUserFromPage(),
};

var commandHandler = WindsorContainer
    .Resolve<ICommandHandler<RegisterAssetCommand>);

commandHandler.Handle(command);

Note: If possible, move the responsibility of getting a commandHandler out of the presentation class and inject it into the constructor of that class (constructor injection again).

No you can create an implementation of the ICommandHandler<RegisterAssetCommand> like this:

public class RegisterAssetCommandHandler
    : ICommandHandler<RegisterAssetCommand>
{
    private ILawbaseAssetRepository lawbaseAssetRepository;
    private IAssetChecklistKctcPartRepository assetRepository;

    public RegisterAssetCommandHandler(
        ILawbaseAssetRepository lawbaseAssetRepository,
        IAssetChecklistKctcPartRepository assetRepository)
    {
        this.lawbaseAssetRepository = lawbaseAssetRepository;
        this.assetRepository = assetRepository;
    }

    public void Handle(RegisterAssetCommand command)
    {
        // Optionally validate the command

        // Execute the command
    }
}

Optionally, you could perhaps even leave the User out of the RegisterAssetCommand by injecting a IUserProvider in the RegisterAssetCommandHandler. The IUserProvider interface could have an GetUserForCurrentContext that the handler can call.

I hope this makes sense.

like image 136
Steven Avatar answered Sep 25 '22 13:09

Steven