4

I'm developing a desktop application in .Net that follows a plugin architecture, something like this:-

I have a "core" .Net solution, containing the desktop exe project, and a handful of class library projects. These classes provide all sorts of shared/common functionality, and are referenced by the application exe and individual modules.

Each module lives in its own .Net solution. The solution has its own copy of the above shared library DLLs that the module's project(s) reference.

End-user installation really just involves deploying the core exe & DLLs, along with the modules' DLLs required at that site, into one folder. I've got no problem with the mechanism used by the core exe to "discover" which modules are present, then load & initialise them.


My concerns are around managing and deploying the DLLs. In an ideal world I should be able to make a change to "module X" and redeploy just that module's DLLs. However it's feasible that a change may also involve updating one of the shared libraries. When I redeploy these updated DLLs, the updated shared library functionality could break other modules (or even the application exe) that reference it. I guess what I should be doing in this scenario is to rebuild/redeploy all solutions, not just "module X".

An easier approach may be to treat the "core" and all modules as a single product, and redeploy everything in every release, even if it's just a change to one module. But it feels like I would be losing one of the advantages of a plugin architecture - I should be able to release a new version of a module in isolation.

Any thoughts? Or are such DLL referencing issues an unavoidable part of using a plugin architecture?

4

I would take an approach that shared libraries are the responsibly of the core, and that modules can be updated however they want on an individual basis except when that requires an update to a shared library.

Additionally, I would track versions of the libraries by keeping a single version number for the core platform which is incremented at each release, and by having modules know which core version they require. This would make tracking down version mismatch issues simpler.

The advantage of this is that module development can carry on mostly independently of the core platform and each other, hopefully with only occasional updates required to the core.

2

As long as you are the only vendor for all "plugins", and as long the invidual modules don't need hundreds of MB disk space, I suggest you put always all DLLs into every released package, and treat everything as one product, with one version number. This makes deployment easier, since you don't need separate packages for partial updates, and if you need a complete reinstall of the newest version, you can use exactly the same package.

However, if you are going to use your plugin architecture to let your modules get their own lifecycle, mostly independent from the core, plan for modules as if they were provided by a different vendor. Make sure there is a stable interface between the modules and the core, and make sure that the modules' libraries don't collide with the libraries of the core (for example, avoid installing them to the same folder). Even if you have a shared library which is used in the core and reused in one of your modules, make sure the core and the modules can use different versions of that shared library simultanously. And make sure your product and your modules have their own version numbers. Then you can deploy your core and your plugins separately.

The second option will typically involve more effort in configuration management than the first, so just "because you have a plugin architecture and can treat your modules as different products" does not mean you must do so, and that you lose something important when deploying everything in one package.

1

Three options for you, pick the one you like more :)

  1. Inject any dependencies from the framework

    When your framework instantiates the plugin, it should pass any necessary framework APIs to the plugin as injected interfaces. That way you can be certain that the plugin is using a version that is compatible with your framework.

  2. Use process isolation

    Instantiate each plugin in its own process. While this may seem like ridiculous overkill, it is also pretty darned bullet-proof. Even if the plugin crashes outright, it won't take down your framework. You can even launch each plugin under a different user context, which may also help protect you from malicious plugins (see Principle of Least Privilege). This option will allow plugins to work with different versions of the same DLLs, and will even allow different versions of the .NET framework.

  3. Require plugin certification

    All plugins must be submitted to a server (it can be automated if you like) that "certifies" the DLL is compatible with the framework (perhaps with a few basic automated tests). After the plugin is validated, it can be strongly named and signed with your private key so that the framework can tell whether it has been tampered. Modify your framework to only allow plugins that are signed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.