Я думаю, вы должны изучить проектирование по контракту . Таким образом, все компоненты будут полностью изолированы друг от друга во время компиляции. Но это переключит работу на поддержание написанных вручную контрактов вместо интерфейса кода, проверенного во время компиляции.
По сути, вы будете иметь одно и то же объявление интерфейса на обеих сторонах провода, а затем они будут автоматически подключены агентом, который является либо пользовательской реализацией Castle DynamicProxy, а также некоторыми компонентами Reflection или контейнером IoC, например Ninject , который поддерживает сервисные локаторы.
По сути, вы пишете «контракт» как для Module1.dll, так и для Module2.dll
public interface IFooProvider {
void Foo GetFoo();
}
Затем вам понадобится реализация сервисного локатора, которая является центральным фрагментом кода, который поможет модулю зарегистрировать себя, и потребитель найдет зарегистрированный модуль. Это нормальная работа упомянутого выше контейнера DI или IoC.
Это будет выглядеть так или иначе так:
public interface IServiceLocator {
object LocateProvider<ContractType>();
void RegisterProvider<ContractType>(object implementation);
}
По сути, Module1 при загрузке должен зарегистрироваться в ServiceLocator вместе с контрактом, который он предоставляет, а затем Module2 при загрузке вызовет метод LocateProvider, предоставляющий требуемый контракт, и получит реализацию Module1.
Что-то стоит:
public class Module1Implementation : IProviderContract {
void Foo GetFoo() { return new Foo(); }
}
public class Main {
public void Main() {
var locator = ServiceLocator.GetLocator();
locator.RegisterProvider<IFooProvider>(new Module1Implementation());
}
}
А в Module2.dll:
public class Consumer {
public IFooProvider FooProvider { get; set; }
public Consumer() {
var locator = ServiceLocator.GetLocator();
FooProvider = locator.LocateProvider<IFooProvider>();
// if Module1.dll is loaded, the locator should supply
// Module1Implementation for you
}
}
Тогда все «поставщики» модуля должны ссылаться на DLL локатора службы, которая должна представлять собой 1 дополнительную DLL, или она может быть встроена непосредственно в основное приложение. А при загрузке каждый модуль должен зарегистрироваться в ServiceLocator вместе с «контрактом», который они представляют.
Затем, последняя часть головоломки, это то, как вы делаете контракты похожими, которые определены в отдельных библиотеках, чтобы они выглядели одинаковыми, поэтому я упомянул Castle DynamicProxy и Reflection. Как это сделать - это уже не так давно, поэтому я оставлю это Google. : -)
Как это поможет? Все зависимости между всеми DLL, но реализация локатора службы удалены. Таким образом, ваша головная боль при развертывании уменьшена до:
- Убедитесь, что реализация локатора службы надежна, поскольку это единственное, что не может быть легко изменено.
- Убедитесь, что компоненты, которые должны взаимодействовать друг с другом, имеют один и тот же "контракт"
- Если контракт меняется, используйте Фасад для обеспечения обратной совместимости.
Уф! Это было довольно длинно и модно, но я надеюсь, что это ответит на ваш вопрос.