none
Modules or plugin arquitecture dependencies RRS feed

  • Question

  • Hello,

    I'm building a plugins/modular application using System.ComponentModel.Composition;

    MyApp -> Load Modules
    Module1 -> DoWork
    Module2 -> DoWork
    Module... -> DoWork

    I don't want reference dependencies on modules. I wan't provide a list of 3rd party dependencies on MainApp that is shared by Modules, but to use them on modules, they must be loaded on each module too. Will not be a problem since MainApp already loaded that dependencies and modules will share them.

    The problem will be when a dependency is recompiled, on MainApp is OK, but modules using a old compiled version will trigger exceptions saying the dependency version is not the same (I think).

    One solution is include all dependencies sourcecode in MainApp, that way modules don't need include a dependency, the problem is some aren't opensource, others are huge and contains multi projects...

    For example include WebKit on MainApp, and all modules can access WebKit without reference is own dll on each module.

    There are any good solution for that trouble?

    Thanks


    sn4k3

    Tuesday, June 18, 2013 6:16 PM

Answers

  • You'll only run into an issue if the dependencies are strongly named and you don't provide a publisher policy.  If your dependencies are strongly named then you can use publisher policy to redirect requests for older versions of an assembly to a newer version.  This is how a lot of standard libraries are shipped.  Note that compatibility isn't guaranteed in newer versions so you'll still need to ensure the modules work with the newer version.

    Also note that in most extensible apps the only shared code are interfaces so in general a contract assembly is not likely to change even if the main app does.  Often what does change is the implementation class that an app returns for a contract.  Since modules shouldn't be working against implementations you can make changes without having to recompile the contract or modules.  Of course if your implementation and contract assemblies are one and the same then you'll run into an issue but publisher policies are available for that.

    I recommend that you read up on how MEF works with upgrades to get a better feel for how you'll need to architect your app to handle future versions.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    • Marked as answer by knive1 Tuesday, June 18, 2013 10:52 PM
    Tuesday, June 18, 2013 6:34 PM
    Moderator
  • If you require that modules derive from Module in order to do pre-init then you really aren't contract based.  It is one thing to provide a base implementation that can be used and quite another to require it.  If you require a base class then the interface is not giving you any real benefit and your Module is the actual contract.

    As for the shared libraries you are going to run into an issue irrelevant of your approach.  If you force all libraries to use the same libraries then you'll cut down on space but all your modules will be inherently limited to the version you're shipping.  This may or may not be a big deal.  Conversely if you allow each module to have their own dependencies then you have a far looser coupling.  However the CLR isn't going to load duplicate assemblies by default so you might run into issues when multiple modules try to load the same assembly.  It really depends upon how you're keeping your modules sandboxed. 

    If you load each module into its own app domain then each module can have any dependencies it wants and you have a loosely coupled architecture.  This is, in my opinion, the best way to go but it complicates the architecture and can have performance issues.  It depends upon how inter-dependent modules can be.  If modules are loaded into the same domain then they will impact each other as far as dependencies go and there is very little you can do about it.

    As for common libraries you might consider implementing the IServiceProvider interface in your app and providing "out of the box" services for common needs.  Your main app is then responsible for hosting and controlling core services while modules can provide their own services.  Going back to VS as an example you'll find that it already has a service interface for working with windows but a VS package can add additional services if needed.  This goes back to loose coupling.

    In an ideal world your app will provide the framework, sandbox and core services that all modules will need.  Each module should be runnable irrelevant of other modules and shouldn't be allowed to compromise the stability of the system.  What libraries each module requires shouldn't impact any other module and therefore should be the responsibility of the module to provide.  As your app evolves you'll likely find services/code that many modules use and these will become candidates for moving into the core app.  The shared set of services your app provides will likely reside in the shared appdomain that all domains in your app use while each module (or perhaps set of) will reside in their own sandbox domain.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    • Marked as answer by knive1 Tuesday, June 18, 2013 10:52 PM
    Tuesday, June 18, 2013 9:40 PM
    Moderator

All replies

  • You'll only run into an issue if the dependencies are strongly named and you don't provide a publisher policy.  If your dependencies are strongly named then you can use publisher policy to redirect requests for older versions of an assembly to a newer version.  This is how a lot of standard libraries are shipped.  Note that compatibility isn't guaranteed in newer versions so you'll still need to ensure the modules work with the newer version.

    Also note that in most extensible apps the only shared code are interfaces so in general a contract assembly is not likely to change even if the main app does.  Often what does change is the implementation class that an app returns for a contract.  Since modules shouldn't be working against implementations you can make changes without having to recompile the contract or modules.  Of course if your implementation and contract assemblies are one and the same then you'll run into an issue but publisher policies are available for that.

    I recommend that you read up on how MEF works with upgrades to get a better feel for how you'll need to architect your app to handle future versions.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    • Marked as answer by knive1 Tuesday, June 18, 2013 10:52 PM
    Tuesday, June 18, 2013 6:34 PM
    Moderator
  • Thanks for the anwser :)

    I will read more about MEF.

    Its a desktop application, i'm using an interface (IModule) and an abstract class (Module : IModule), all plugins extends Module since i need some pre-functions in it.

    App <- App.Commun -> App.Module.Test : Module 
      ^----------------------------------------------^

    The trouble is not the interface at all, the project is a little bit wide and open (any person can develop a module for example) and some modules will require many 3rd party libs like IniParser, HtmlParser, etc. for do some of the work they require to, and i don't wan't modules publish assets (.dlls) in order to work.

    So i'm thinking in prepare some pre-included Libs on App that most will use. That will avoid ModuleA Publish a HtmlParser.dll and ModuleB Publish HtmlParser.dll and avoid assembly conflicts.

    Currently i have that folder structure:

    C:\PATH\App.exe
    C:\PATH\App.Commun.dll
    C:\PATH\modules\*.dll (Modules Directory)
    C:\PATH\modules\*.settings.xml (Modules Settings)

    OR should i create one folder per module? That way they can publish its own reference dependencies

    Thanks again for the the support :)

    • Edited by knive1 Tuesday, June 18, 2013 7:16 PM
    Tuesday, June 18, 2013 7:13 PM
  • If you require that modules derive from Module in order to do pre-init then you really aren't contract based.  It is one thing to provide a base implementation that can be used and quite another to require it.  If you require a base class then the interface is not giving you any real benefit and your Module is the actual contract.

    As for the shared libraries you are going to run into an issue irrelevant of your approach.  If you force all libraries to use the same libraries then you'll cut down on space but all your modules will be inherently limited to the version you're shipping.  This may or may not be a big deal.  Conversely if you allow each module to have their own dependencies then you have a far looser coupling.  However the CLR isn't going to load duplicate assemblies by default so you might run into issues when multiple modules try to load the same assembly.  It really depends upon how you're keeping your modules sandboxed. 

    If you load each module into its own app domain then each module can have any dependencies it wants and you have a loosely coupled architecture.  This is, in my opinion, the best way to go but it complicates the architecture and can have performance issues.  It depends upon how inter-dependent modules can be.  If modules are loaded into the same domain then they will impact each other as far as dependencies go and there is very little you can do about it.

    As for common libraries you might consider implementing the IServiceProvider interface in your app and providing "out of the box" services for common needs.  Your main app is then responsible for hosting and controlling core services while modules can provide their own services.  Going back to VS as an example you'll find that it already has a service interface for working with windows but a VS package can add additional services if needed.  This goes back to loose coupling.

    In an ideal world your app will provide the framework, sandbox and core services that all modules will need.  Each module should be runnable irrelevant of other modules and shouldn't be allowed to compromise the stability of the system.  What libraries each module requires shouldn't impact any other module and therefore should be the responsibility of the module to provide.  As your app evolves you'll likely find services/code that many modules use and these will become candidates for moving into the core app.  The shared set of services your app provides will likely reside in the shared appdomain that all domains in your app use while each module (or perhaps set of) will reside in their own sandbox domain.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    • Marked as answer by knive1 Tuesday, June 18, 2013 10:52 PM
    Tuesday, June 18, 2013 9:40 PM
    Moderator
  • Great article! :)

    Thanks for your feedback, it is very important and outstanding

    Also if you have some real examples or articles that you can share about this topic i will appreciate


    sn4k3

    Tuesday, June 18, 2013 10:57 PM