Plugable module architecture design and thoughts about its implementation

Introduction

Software’s complexity grows as time goes by which is suitable to describe ZStack now. Quantities of features has been integrated in the past years and developers suffered on customizing features for some commercial reasons which pushed us to think about how to get rid of customizing feature efforts and focus on core function to earn more technical advantages.

Generallly, customizing features is harmful when compared to normal features (but maybe it can get commercial benifits so do not take this as absolute rule), because more customers use normal features.

But because some integration requests already designed which not only require maintainance but also need development for new integration. For example, a feature for security is desing to use third-part machine to do encryption,but when customer use the feature, they all use different machine which caused lots of development cost for us to do the integrations.

In these cases, a plugable architecture is required to cut down the cost and this is the reason to write this blog to record related design and make it a repeatable practice.

Requirements

For existing systems which need to support multi types of resources, typically use a register way to connect the management layer and application layers. And the application layers use a couple of standard interfaces to keep consistency and seperate implementation from mechanism.

So for the aim of delveloping a plugable module architecture if we still follows the pattern, easily we will be distrubed by the change of core codes from the mechanism. For example, if the core changes the mechanism as a result, application should change all of the interfaces as expected, so development is required which is not satisfied with out target.

Just after that small think, list all requirements before starting design. Following points are the aim of this architecture:

  • Low development efforts or configuration as a service.
  • No core code dependency. Any changes to mechanism should be avoid.
  • Security. Plugin module should not cause any security issue.

Base on those points, the architecture should be changed to expose its mechanism by interfaces.

For example

  1. read plugin configuration and verify it
  2. generate code proxy according to the configuration
  3. limited db access controled by the code proxy
  4. Invokations can be verified by unit test to check the plugin can work as expected
  5. change the configuration the refresh it could update the code proxy
  6. plugin configurations use a specified syntax
  7. if code proxy support runtime refresh can be configured

From writing configurations, functional features can be easily supported.

If any requests need data structure support, a configuration map shoud be support for more customize and in this way we can easily create a http client to support some modules.

But if customize feature use any third-part libs, only configuration is not enough to support.

For code level, maybe open source interfaces is still needed.

We should offer read only data structure and functional interfaces but should not involve core codes of Java which in most cases, are aspectj support, spring containers and so on. Only pure java should be used for all plugin modules.

In next section, we will raise examples of two module, one is not suitable for plugable module and other one is suitable for plugable.

Login module

Multiple login methods should be supported because many standard third-part authentication is already exists for example, CAS, oauth2, ldap and so on.

If you already have a customized system for authentication, I seems diffcult to integrate another system with existing one. Because the authentication actually need to transfer sensitive information to do authenticate but which is not safe enough to expose those data for plugin exactly.

From the usage of CAS, oauth2 we can see a generally authentication service just redirect authentication to itself and returns a authentication result and finally redirect to the right page. So sensitive information do not transferred but use a token to verify client’s response instead.

For ldap, just access its database directly to verify if the user is exists and finish authentication. Because ldap just integrated with ZStack so also no data transferred but ldap module itself need to care about the security issue which already resolve by third-part libs.

So when we tries to support plugin for login, password or any key liked information need to be exposed which is quite unsafe and have potential security risks. Compared to ldap verification, password is used directly and also faces the same problem.

For login module integration, two ways are recommended. One is using open-source authentication like CAS or oauth2. Another is integrating ZStack API to use account/user directly.

Following figure shows the flows of login module

  • Support plugable system, main auth or additinal auth should be exposed
  • Sensitive information go through the whole flow
  • CAS and oauth2 use their own authentication which is not related the current architecture

Alarm/Event system

For alarm event system, only expose monitor messages which seems more suitable to be developed as a plugable system.

When have lots of endpoint support, some use stantard libs like dingtalk and microsoft teams, offers api for message sending. But when sms system require integration, this became diffcult due to the lack of stantard apis.

So a plugable system can be powerful to reduce development efforts.

For example, sms systems all have their own apis the usages, so the interation of api is the first step. But actually the business scenarios are not only limited on send message. Maybe message distribution strategy is required, and message format is required.

So seperate those parts as followings:

  • Send sms message
  • Distribution strategy
  • Message format
  • ….

When design plugable system, the most important thing is that we should seperate mechanism requirements and strategy requirements.

For message format, actually, all kinds of message required this feature and can be noted as general feature and mechanism and we can also call it customize message format. The format change do not influence how the message send or comsumed.

Distribution strategy, from the name, we can known that no specific rules or format could be announced because the strategy always changes from one the another.

Send sms message. also the integration part but the message with a format is what we can offered and only how to send the message should be defined and which is I think can be seperated from the system.

And more informations maybe, endpoint type, status and those ZStack defined fields also need to be involved in the plugable system so we get a architecture like following:

A plugable endpoint in designed to manage endpoint plugins and plugin should obey following rules:

  • Offer endpoint type
  • Implement interface to send message
  • Unit test for endpoint plugin

And the design of plugable endpoint may like following:

So plugable endpoint should define a interface require type and send() method’s implementation for the usage. Take the interface to a seperate open source package and publish to maven repository, its easy for develop and easy to use. By reflections, endpoint could be initialized from jar so just add jar to your dependency directory is ok.

To develop a plugin for new sns endpoint no need to known the logic or usage of orignal application system but developer could focus on the send method itself.

On the other hand, application level should extend orignal CURD api to suitable for those plugin definition and keep compitable with other endpoints.

TODO

  • Database access is not limited which should add limited on database service itself.
  • Rules for all kind of plugins so that plugins can be managed together and distribution can keep consistency
  • More examples
  • For exsiting modules, try to refactor the plugin types to get rid of module dependency