Service-Based Extensions and Extension Points

Services allow to dynamically (at run time) add functionalities in existing applications without changing the application itself.

The Georito platform is based on a simple implementation of this Service Provider / Consumer pattern.

The main idea of this pattern is that one part of the system can dynamically register, update and un-register some services and other parts of the same system are notified about these changes and they can immediately start to use these services. One service consumer can use multiple services and the same service can be used by multiple consumers:

In the real life service consumers can be compared with computers equipped with USB slots:

USB slots allow us to plug dynamically new functionalities to the system without changing the system itself.

And the external devices that can be plugged in the USB slots can be compared with service providers:

In this example we can call the USB slot an "extension point" - it are places where external devices can be plugged. We can call USB plugs an "extension". With these extensions new features can be added to the computer.

  • extension point = a slot used to consume the service
  • extension = a plug used to provide the service

A similar  approach based on service providers and service consumers (with extension points and extensions) can be implemented in the software world. It can be useful to dynamically enhance existing applications without changing the applications themselves.

For example if core developers foresee "extension points" in their application to add sharing possibilities, then external developers can provide in the future new extensions sending information to the social media, via mail, enable visualizing on external devices, etc.

When a new service is registered, removed or updated by a provider in the system then all consumers are notified about these changes. This approach allows to implement dynamic (“late”) binding of individual pieces of the system and to provide new features without even re-starting the application.

Our service model was inspired by the Java OSGi (Open Services Gateway initiative) standard, widely used by the Eclipse ecosystem. Another example of a system implementing late-binding based on services is the Android Operating System with notions of “intents”.

Services and UI

The architecture can be used to provide and use (consume) any kind of service. Each service can be a function, object or a literal value. In our case we are using services to implement an extensible visualization system. Each view can declare extension points - places where new visual elements can be inserted by other parts of the system. For example the main visual block defining the application layout can declare extension points for a page header, a main body and a sidebar. Individual screens provide headers and body extensions and visual components, which should be inserted in the layout.

The approach is opposite to the classical way of building user interfaces.

Classical web applications are top-down systems where parent components define which child elements should be visualized. The resulting application is a "fusion" of all these elements into one final monolithic package containing compiled JavaScripts + HTML + CSS... On the schema below each visual part of the UI is shown with its own color:

Classical approach: all components are integrated into one block

This top-down approach means that to update even a small component of the application, the whole package should be re-build and re-deployed. It represents a very important amount of work, especially if the same small component is used by many applications:

To fix a bug in one module all applications using this module should be re-compiled and re-deployed 

In contrast with this classical approach, applications based on services (extensions/extension points) become much more "dynamic", easily extendable and maintainable:

Extension-based approach - each component is inserted dynamically in dedicated slots

The same components can be used in multiple layouts without changes.

This feature is especially important when critical bugs are discovered:

With extensions only the updated component is re-compiled and re-deployed. 

With extensions only the updated component is re-compiled and re-deployed. All blocks of the application will use the fixed version automatically.


Conclusion

Applications based on service consumers / providers are much more extendable and maintainable. Individual components of the system based on services (on extensions / extension points) are bound together at run-time.

This approach allows:

  • Dynamically (at run-time) add, remove or update  various application features.
  • Life cycle of containers defining extension points are detached from extensions.

Some examples of practical advantages of this approach:

  • Bug fixing: we don’t need to rebuild the whole application when we only changed a small part of this application
  • One layout can be used for multiple application extensions and multiple layouts can be used for the same functional blocks without recompilation of layouts or blocks

For more details and examples you can check these ObservableHQ Notebooks: