The plugin architecture of the Open Targets Platform

Plugin, you may have heard the word before, but do you know why and how we use them?

The main reason we have a plugin architecture is for flexibility and easier customisation of our content, which can be either a simple table or a more complex visualisation.

In early versions of the Platform, we used to code the list of content of our pages into the page itself. Although the content loaded fine, we found two major drawbacks with that approach:

  • It was fiddly for our partners to customise their content

  • It had an increased download time as all the code required for the content was loaded on the initial page load

We had to move on and decided to try to load the visualisations (and their relevant code) as and when needed. In other words, we wanted to display the visualisations only if our users navigated to that page containing the visualisation. This is known as lazy loading.

To enforce this, we decided to structure the pages around an accordion UI component, where all sections with their clear headings are collapsed.
You can see the accordion in the profile page of any of our targets e.g. BRAF, or diseases, e.g. Palpebral lentiginosis.

If (and only if) a user expands any of these sections in the accordion, then the contained plugin will be loaded and displayed.

This new approach is a no-brainer, as it allows for:

  • Optimised loading of code

  • Optimised loading of time

  • Increased flexibility to customise content

Implementing the plugins

We organise the Platform code by function, and the plugins into directories (plugins/), each of them containing all files required by its plugin.

The directives

A plugin in our Platform is a regular AngularJS directive that could display anything like:

  • a table
  • a block of text
  • an external widget developed elsewhere
  • a graph or visualisation implemented in D3.js
  • data available on the page, or by downloading new information

The Plugin Loader

The interesting bit here is to see how these plugins (directives) are loaded.

We use SystemJS to allow the loading of code on the fly, and otPluginLoader for dynamic load of the plugin when we first land on the page.

This means each plugin file (JS, HTML) is only loaded when the plugin is displayed.

<ot-plugin-loader  
    target="search.info.gene" 
    disease="search.info.efo" 
    plugin="section.element" 
    dependencies="section.dependencies" >
</ot-plugin-loader>

The otPluginLoader directive takes the following options:

  • target object
  • disease object
  • plugin
  • dependencies

JSON

We configure our pages and plugins in a JSON file under the general config section:

- app
    - config
        - general
            - default.json

Here we define the plugins for each page as:

"diseaseSections": [
    // plugins for the disease profile page
    ...
],
"targetSections": [
    // plugins for the target profile page
    ...
    {
        "name": "relatedTargets",
        "element": "ot-related-targets",
        "heading": "Similar targets (based on diseases in common)",
        "new": true
    }
]

For any given plugin, we specify:

  • the name of the plugin
  • the name of the directive (known as element)
  • the heading to be displayed for the plugin in the accordion
  • the dependencies (if any) to be loaded for this plugin
{
    "name": "proteinStructure",
    "element": "ot-pdb-target",
    "heading": "Protein Structure",
    "dependencies": {
        "vendor/bio-pv.min.js": {
            "format": "global"
        }
    }
}

For any additional dependencies, we also specify them in the JSON, which will be lazy loaded before the content display.

Setting up the page

Once we create and configure a plugin, this needs to go on a page. Typically a plugin-based page is built on an accordion, so a plugin is loaded when the relative section is expanded.

<uib-accordion>  
    <div uib-accordion-group ng-repeat="section in sections">
        ...
        <ot-plugin-loader></ot-plugin-loader>
        ...
    </div>
</uib-accordion>  

The page loops through the plugins defined in the config JSON file and populates the accordion: a plugin-loader directive is created for each section.

Done and dusted

Funky visualisations can indeed be quite a complex task to develop. We have found that if we add them to the Platform as a plugin, our lives become a tiny bit easier.

Are there any visualisations or plugins you would like to see in the Open Targets Platform?

Do get in touch and we will do our best.