Implementing a plugin architecture / plugin system / pluggable framework in Angular 2, 4, 5, 6

Anthony Gatlin picture Anthony Gatlin · Jan 3, 2017 · Viewed 27.8k times · Source

Update 5/24/2018: We are now +3 versions of Angular from my original post and still don't have a final workable solution. Lars Meijdam (@LarsMeijdam) has come up with an interesting approach which is certainly worth a look-see. (Due to proprietary issues, he had to temporarily remove the GitHub repository where he had originally posted his sample. However, you may message him directly if you would like a copy. Please see the comments below for more info.)

Recent architectural changes in Angular 6 do bring us closer to a solution. Additionally, Angular Elements (https://angular.io/guide/elements) provides some component functionality--though not quite what I originally described in this post.

If anyone from the amazing Angular team happens to come across this, please note that there seem to be many other people who are also very interested in this functionality. It might well be worth considering for the backlog.


I would like to implement a pluggable (plug-in) framework in an Angular 2, Angular 4, Angular 5, or Angular 6 application.

(My specific use case for developing this pluggable framework is that I need to develop a miniature content management system. For a number of reasons not necessarily elaborated here, Angular 2/4/5/6 is a near perfect fit for most of the needs of that system.)

By pluggable framework (or plug-in architecture), I specifically mean a system which allows third party developers to create or extend the functionality of a primary application through the use of pluggable components without having direct access to or knowledge of the primary application's source code or inner workings.

(That phrasing about "without having direct access to or knowledge of the application's source code or inner workings" is a core objective.)

Examples of pluggable frameworks include common content management systems like WordPress or Drupal.

The ideal situation (as with Drupal) would be to simple be able to place these pluggable components (or plug-ins) into a folder, have the application auto-detect or discover them, and have them just magically "work." Having this occur in some sort of hot-pluggable manner, meaning while the app was running, would be optimum.

I am currently trying to determine answers (with your help) to the following five questions.

  1. Practicality: Is a plugin framework for an Angular 2/4/5/6 application even practical? (Until now, I have not found any practical way to create a truly pluggable framework with Angular2/4/5/6.)
  2. Expected Challenges: What challenges might one encounter in implementing a plugin framework for an Angular 2/4/5/6 application?
  3. Implementation Strategies: What specific techniques or strategies could be employed for implementing a plugin framework for an Angular 2/4/5/6 application?
  4. Best Practices: What are the best practices for implementing a plugin system for an Angular 2/4/5/6 application?
  5. Alternative Technologies: If a plugin framework is not practical in an Angular 2/4/5/6 application, what relatively equivalent technologies (e.g. React) might be suitable for a modern highly reactive Web application?

In general, use of Angular 2/4/5/6 is very desirable because:

  • it is naturally extremely fast--blazingly so.
  • it consumes very little bandwidth (after the initial load)
  • it has a relatively small footprint (after AOT and tree shaking)--and that footprint continues to shrink
  • it is highly functional, and the Angular team and community are continuing rapid growth of its ecosystem
  • it plays well with many of the best and latest Web technologies such as TypeScript and Observables
  • Angular 5 now supports service workers (https://medium.com/@webmaxru/a-new-angular-service-worker-creating-automatic-progressive-web-apps-part-1-theory-37d7d7647cc7)
  • being backed by Google, it is likely to be supported and enhanced well into the future

I would very much like to use Angular 2/4/5/6 for my current project. If I am able to use Angular 2/4/5/6, I will also be using Angular-CLI and probably Angular Universal (for server-side rendering.)

Here are my thoughts, so far, regarding the questions above. Please review and provide your feedback and enlightenment.

  • Angular 2/4/5/6 apps consume packages--but this is not necessarily the same as allowing plugins within an application. A plugin in other systems (e.g. Drupal) can be essentially added by dropping the plugin folder into a common modules directory where it is automatically "picked up" by the system. In Angular 2/4/5/6, a package (as a plugin might be) is usually installed via npm, added to the package.json, and then manually imported into the app--as in app.module. This is much more complicated than the Drupal method of dropping a folder and having the system automatically detect the package. The more complicated it is to install a plugin, the less likely people will be to use them. It would be much better if there was a way for Angular 2/4/5/6 to automatically detect and install plugins. I am very interested to find a method which allows non-developers to install the Angular 2/4/5/6 application and install any chosen plugins without having to understand all of the application's architecture.

  • Generally, one of the benefits of providing a pluggable architecture, is that it is very easy for 3rd party developers to extend the functionality of the system. Obviously, these developers will not be familiar with all of the intricacies of the code for the application they are plugging into. Once the plugins are developed, other even less technical users may simply install the application and any selected plugins. However, Angular 2/4/5/6 is relatively complicated and has a very lengthy learning curve. To further complicate things, most production Angular 2/4/5/6 applications also utilize Angular-CLI, Angular Universal, and WebPack. Someone who is implementing a plugin would probably have to have at least some basic knowledge of how all of these fit together--along with a strong working knowledge of TypeScript and a reasonable familiarity with NodeJS. Are the knowledge requirements so extreme that no third party would ever want to develop a plugin?

  • Most plugins will likely have some server side component (e.g. for storing/retrieving plugin related data) as well as some client-side output. Angular 2/4/5 specifically (and strongly) discourages developers from injecting their own templates at runtime--as this poses a serious security risk. In order to handle many types of output that a plugin may accommodate (e.g. display of a graph), it appears that allowing users to create content which is injected into the response stream, in one form another, is probably necessary. I wonder how it might be possible to accommodate this need without figuratively shredding Angular 2/4/5/6's security mechanisms.

  • Most production Angular 2/4/5/6 applications are pre-compiled using Ahead of Time (AOT) compilation. (Probably all should be.) I am uncertain how plugins might be added to (or integrated with) pre-compiled applications. The best scenario would involve compiling the plugins separately from the main application. However, I am uncertain how to make this work. A fallback might be to re-compile the entire application with any included plugins but that complicates things a bit for an administrative user who simply wants to install the application (on his own server) along with any selected plugins.

  • In an Angular 2/4/5/6 application, especially a pre-compiled one, a single piece of errant or conflicting code can break the entire application. Angular 2/4/5/6 applications are not always the easiest to debug. Application of ill-behaved plugins could result in very unpleasant experiences. I am currently unaware of a mechanism to gracefully handle ill-behaved plugins.

Answer

yurzui picture yurzui · Apr 3, 2019

Update

For Angular 11 I strongly recommend you to take a look at implementation with Webpack 5 Module Federation

🎉 https://github.com/alexzuza/angular-plugin-architecture-with-module-federation

Previos version

🛠️ Github demo angular-plugin-architecture

Maybe Ivy can change something but for the time being I use the solution that uses Angular CLI Custom Builder and meets the following requirements:

  • AOT
  • avoid duplicate code(packages like @angular/core{common,forms,router},rxjs,tslib)
  • use shared library in all plugins but DO NOT SHIP generated factories from that shared library in each plugin but rather reuse library code and factories
  • the same level of optimization that Angular CLI gives us
  • for importing the external modules we just need to know only one thing: their bundle file path
  • our code should recognize module and place plugin into the page
  • support server-side rendering
  • load module only when needed

The usage is simple as:

ng build --project plugins --prod --modulePath=./plugin1/plugin1.module#Plugin1Module 
         --pluginName=plugin1 --sharedLibs=shared --outputPath=./src/assets/plugins

More on this in my article: