Skip to content

Architectural Decision Records (ADRs)

Louise Davies edited this page Jun 25, 2020 · 21 revisions

This page provides all the architectural decisions for the system - this may be decisions where there are multiple valid choices but for certain reasons one option is chosen over the others.

This list of ADRs also retains historical decisions to show how decisions have changed over time and the reason for that change - this prevents cyclical decisions due to knowledge being lost.

Possible status values are proposed, accepted, rejected, deprecated, superseded.

ADRs

1. The frontend technology will be React

Status: accepted

Context: There are multiple frontend technologies available; React, Vue or Angular - one of them needs to be chosen to write the SciGateway frontend and its plugins in.

Decision: React will be the chosen technology for the frontend as it is best suited to large scale apps, has the most community support and combines well with other technologies allowing it to stay current.

Consequences: The SciGateway frontend and any plugins centrally developed will be developed using React.


2. The frontend will be developed with Typescript

Status: accepted

Context: Javascript is an dynamically typed language. Typescript is an extension to the the ECMAScript language specification that strongly types the language.

Decision: We have decided to adopt the Typescript for frontend development. This will add additional development and compile time checks to the code.

Consequences: Additional development overhead.


3. Redux for React App State Store

Status: accepted

Context: Building a complex web application brings challenges around how to manage state.

Decision: We have decided to adopt the Redux architecture to provide a clean separation between our views, actions and state store.

Consequences: No foreseen consequences of this choice.


4. The frontend will use Material-UI components

Status: accepted

Context: There are multiple UI widget libraries available:

Decision: Material-UI will be used for a standard component library.

Consequences: The library

  • implements Google's material design,
  • offers a wide range of standard widgets,
  • has very good online documentation,
  • is actively supported,
  • npm libraries available that offer integration with Redux react-redux
  • is a de-facto industry standard for React UIs

5. Micro-frontends

Status: accepted

Context: The application must support a plugin architecture supporting site-specific front end components. A micro-frontend architecture supports this model bringing in UI elements from local files or remotely hosted sites.

Decision: We have decided to adopt a micro-frontend architecture using the single-spa library.

Consequences: All UI plugins must include a set of hooks defined in the single-spa model:

  • mount
  • unmount
  • bootstrap.

6. Namespaces will be used to partition plugin and parent application events

Status: accepted

Context: Messages are shared between plugins and the parent application via CustomEvents with typeArg of scigateway. The action wrapped by this event is dispatched as a redux action in the parent app, and if the broadcast property is set, in any other plugin that is listening.

The redux action triggered uses the action.type attribute of the event payload.

There is the potential for name clashes between actions defined in plugins for internal use, for consumption by external plugins or by the parent app.

Decision: We have decided that all event actions must adhere to a namespace pattern: <plugin_name|scigateway>[:api]:<action_name>.

  • The core API of the parent application will be defined through scigateway:api actions.
  • The public API of any plugin must include the :api element.
  • All plugins must be uniquely named.
  • Event names will be underscore_separated_words

Examples include:

  • scigateway:api:register_route : plugin registration action, published by the parent app for use by all plugins
  • scigateway:toggle_drawer : internal parent application redux event
  • plugin1:api:request : public action owned by plugin1
  • plugin1:process_action : private redux action for plugin1

Consequences: Provided all plugins are uniquely named there can be no conflict of actions triggered by plugins. There is a clear indication whether an event should be handled or ignored. There is no requirement for internal messages to remain consistent between versions however breaking changes to any public action should be well publicised before being made.


7. Material-UI will not be provided as an external library

Status: accepted

Context: One of the drawbacks of micro-frontends is that the overall size can get large if they all include copies of the same library. For this reason the parent app includes a copy of common libraries (e.g. React and ReactDOM) that plugins can use.

Decision: We will not make Material-UI available via the parent app. We tried this and it made the referencing very difficult in the source code (it couldn't use the normal import syntax and had to be accessed from window[material-ui]. The complexity this would have introduced onto plugin developers was deemed to be too great and not worth the space savings at this point in time.

Consequences: All plugins will need to include their parts of Material-UI - this will mean repeated instances of parts of the library and could lead to a large overall bundle size. This should be monitored and this ADR should be revisited if it becomes an issue.


8. Google analytics will be used to track page views and some user actions

Status: accepted

Context: In order to be able to log what users commonly do better than the current TopCAT, we need to log pageviews and any actions that we would like to be able to how they are being used.

Decision: We will use Google Analytics via the react-ga library

Consequences: Google analytics will be used to track pageviews and actions. The application will have to obtain user consent to use Google Analytics. We will not be in complete control of our log data - this decision is to be revisited later on if/when a server side logging mechanism is added.


9. datagateway-table: Downloading files will be performed via a link to the IDS in a new tab

Status: accepted

Context: The current TopCAT uses <iframe> to allow users to download files obtained from the IDS. However, if the IDS call fails then this doesn't provide any feedback to the user. In order to improve the user experience in the failure case, the error must be visible in some way.

Decision: We will instead create an invisible link to the IDS and open it in a new tab. This tab then either disappears and shows the user the download dialogue in the success case or displays the IDS error page in the failure case.

Consequences: The user will be able to see if the download call failed. The error appears in a new tab and our application tab is left as it is. We disrupt the user experience by opening new tabs and showing errors in a new tab. This will be fixed in datagateway-table#3, which will then supersede this ADR.


10. Use react-joyride library instead of reactour for the user tour

Status: accepted

Context: We would like to provide a virtual "tour" of the app, where specific buttons/icons are highlighted and tooltips describe what the user can do.

Decision: Initially, the reactour library was suggested, but it was decided to use the react-joyride library instead for two reasons: the first is that it is more popular and thus is more likely to be better maintained. The second reason is that it requires a peer dependency on styled-components - which is a styling solution we don't use and so would unnecessarily bloat the application.

Consequences: The tour will internally use the react-joyride library.


11. datagateway-table: Use react-virtualized and Material-UI table UI components to create our own table component

Status: accepted

Context: We need to have a table component that can fulfil the various user stories we have. Multiple options were considered, see this page for more info: https://github.com/ral-facilities/scigateway/wiki/Table-library-comparison

Decision: It was decided that bespoke libraries were either too immature or they lacked features in the non-commercial versions. The react-virtualized library provides helpers such as being able to evenly size columns and handle lazy loading of rows, as well as providing row virtualization, and the Material-UI library provides the visual components such as TableCell and TableSortLabel that provide the styling. This decision meant that we had to implement things ourselves, such as manually crafting filter components and adding support for row expansion, but the code complexity was low enough that rolling out our own solution was the best idea. It also the most flexible solution, as future features (e.g. column resizing) are always possible.

Consequences:

  • The react-virtualized library is used for virtualization and other helpers
  • Material UI provides visual components
  • Added code complexity due to having to implement features ourselves

12. datagateway-table: Implement our own custom breadcrumb trail component and settings

Status: accepted

Context: We need to have a breadcrumb trail component above the table component, similar to TopCAT, so that the user can track the hierarchy of data in the table as they browse. This component should also be able to be customised for different facilities with regard to the data it can display.

Decision: It was decided that a custom component (PageBreadcrumbs) would be developed to customise the information available to the user via a breadcrumb trail above the existing TableView component. The Breadcrumbs React component provided by Material UI was initially used as a starting point for development, however, this was dropped later due to ease of styling the custom component.

This decision meant that the custom component has a custom way of managing it's internal state and the breadcrumb information it stores about the current page and the pages visited before. It also incorporates the information available via the browser's location in addition with further data from the API. Information displayed from the API can also be customised via breadcrumb settings which can be specified by the application.

This adds further complexity in handling and processing information within the custom component, but provides a working breadcrumb trail as per requirements.

Consequences:

  • A component which meets the requirements and provides settings customised to facilities,
  • Added code complexity as the component is implemented ourselves,
  • Custom styling of the component which does not adhere to the general theme,
  • Does not use the Breadcrumbs component provided by Material UI (the component can be altered to use this in the future).

13. DataGateway: Switch to Yarn instead of NPM for package management

Status: accepted

Context: See Switching to Yarn

Decision: It was decided that the pros outweighed the cons and the native support for monorepo package installation with Yarn Workspaces motivated the switch. In the future, we can switch back to npm - we would just need to add in the lockfiles + lerna bootstrap configuration again + update the READMEs + package.json commands.

Consequences:

  • datagateway now maintains a yarn.lock file instead of package-lock.json files
  • datagateway now assumes yarn is installed when running package.json scripts
  • datagateway uses Yarn Workspaces to handle it's monorepo structure and linking packages together

14. datagateway-api: Use apispec library for OpenAPI spec generation

Status: accepted

Context: Previously, the OpenAPI spec was generated by a script that Kieran wrote. However, this was prone to drifting away from the code/missing information, so using a library to generate it was decided to be better and more foolproof. Multiple libraries were evaluated:

  • flask-restx: looks like it was originally a fork of Flask-RestPlus (the framework we're using) which made it a good candidate. It uses annotations to generate OpenAPI specs. I originally started documenting using it - however I was having trouble performing certain tasks (e.g. recursive models, using OpenAPI v3, documenting complex request parameters like the filter params) and decided it was becoming too onerous to work around these issues.
  • flasgger: Involves more writing of plain OpenAPI specs. Claims to have Flask-Restful support built in which is good. However, I had trouble determining where you would document definitions/schemas/models globally rather than per endpoint. Additionally, it doesn't seem to have any advantages over apispec other than features which we don't use.
  • apispec: Involves more writing of plain OpenAPI specs. Uses "plugins" to support various frameworks, and there exists a Flask Restful one (apispec-flask-restful). Is backed by the Marshmallow organisation which supports a lot of other popular libraries.

Decision: We shall use apispec due to the above reasons (popularity, large backing org, existing integrations etc.)

Consequences:

  • apispec controls our OpenAPI spec generation
  • Our OpenAPI documentation is written as docstrings for the endpoints.

  1. Use react-i18next for internationalisation in DataGateway

Status: accepted

Context: SciGateway uses it's own translation solution, but in the development of DataGateway there were times where more advanced technology (e.g. pluralisation) would be needed - additionally a non-redux solution would be needed for datagateway-download.

Multiple libraries were identified in datagateway#84: i18next/react-i18next, react-intl and polyglot.js.

Decision: react-i18next was chosen over the others. polyglot.js was ruled out for not supporting React officially, meaning we'd have to do this ourself or find a third party library to do so. react-i18next and react-intl are both pretty similar in their offerings, but despite react-intl technically being the more popular react library, react-i18next is still pretty popular, the i18next project is the longest running and most stable since react-intl almost went unsupported due to Yahoo dropping it's support. Additionally, there were some nice features of react-i18next, such as allowing nested translation dictionaries and automatically providing libraries for things like file loading and language detection.

Consequences:

  • datagateway now uses the react-i18next library to translate it's strings.
Clone this wiki locally