OP, I still don't fully understand your design, but I gather that your goals are:
- Pave the way for occasional updates to your business logic
- Minimize impact to third party applications which already reference one of your DLLs,
Library.Core.dll.
Your current plan seems to be to deploy another DLL side by side with Library.Core.dll and leave the original DLL unchanged. This will not work. Unless a DLL is specifically coded to allow it, another DLL can't override its functionality-- that would be a huge burden to developers and would cause a security risk, since anyone could put a DLL out there that breaks an application, potentially in a malicious way.
So you will need to release at least one new version of Library.Core.dll. If you use the same version number and signing key, your clients won't need to recompile. Or if you would like to up the version number (generally recommended), you can tell your clients to use a binding redirect if they don't want to recompile.
Given the above, the new plan is to
- Write one new version of
Library.Core.dll that is interface-identical to the original. This DLL must support future functionality without having to be changed.
- Write a net new
Library.Functionalities.dll that will contain the implementation. You may end up releasing new versions of this in the future.
Here's how I would go about it:
First, write Library.Functionalities.dll using modern IoC techniques, introducing new constructors as needed but keeping the original object model.
Secondly, write a new Library.Core.dll that will be a facade to Library.Functionalities.dll. Here's an example:
[assembly:InternalsVisibleTo("Library.UnitTests")]
namespace Library.Core
{
class User
{
private readonly IUser _innerUser;
public User() : base (Factory.Resolve<IUser>()); //For public consumption
internal User(IUser innerUser) //For your unit testing projects
{
_innerUser = innerUser;
}
public void LogOff()
{
_innerUser.LogOff(); //Wrap the inner user object
}
}
internal class Factory
{
private static MyIoCContainer _container = new MyIoCContainer(); //AutoFac, Unity, Ninject, whatever
static Factory() //Composition root here
{
ContainerBuilder builder = new ContainerBuilder();
Library.Functionalities.Application.RegisterDependencies(builder);
_container = builder.Build();
}
static public T Resolve<T>()
{
return _container.Resolve<T>();
}
}
}
And in Library.Functionalities.dll:
namespace Library.Functionalities
{
public class Application
{
static void RegisterDependencies(ContainerBuilder builder)
{
builder.RegisterType<IUser, User>();
}
}
}
In the above example, Library.Core.User is just a thin wrapper for Library.Functionalities.User. If the caller constructs it using the old method (with no injection), it'll substitute a default injection from a factory.
You can write the factory any way you want. In my example I use a simple IoC container approach.
When you're done, actual resolution of the dependency on User is hidden from the client, who can continue to reference Library.Core.dll, completely unaware he is only talking to a facade.
When you have new functionality to distribute, you can simply update Library.Functionalities.dll, and if you have coded the factory properly the new version should automatically be picked up. And you will still be able to use all the modern IoC techniques, inject shims for unit testing, etc.
var user = new Library.Core.User()or do they usevar user = new Library.Functionalities.User()? Or do they use a factory that you provide?