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?
As Morten points out, move the non injectable dependecies from the constructor call to the method(s) that actually need to use it,
If you have constructor paramters that can't (or are difficult to) be injected you won't be able to autmatically inject
IAssetRegister
into any class that needs it either.You could always, of course, create a
IUserProvider
interface with a concrete implementation along these lines:Thus creating another injectable dependency where there was none. Now you eliminate the need to pass a user to every method that might need it.
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:
Now define an interface for handling business commands:
Your presentation code will now look like this:
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:Optionally, you could perhaps even leave the
User
out of theRegisterAssetCommand
by injecting aIUserProvider
in theRegisterAssetCommandHandler
. TheIUserProvider
interface could have anGetUserForCurrentContext
that the handler can call.I hope this makes sense.