Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 10 Next »

This article summarizes OpenLMIS-UI application architecture, and will be included into the OpenLMIS documentation once it is finalized.
Note: See dev-ui v7 epic for build process information - this document focuses on application logic.  

Overview

The OpenLMIS-UI is a progressive web application that is URL Driven. The application architecture stresses modular Javascript and DRY HTML markup, so implementers can customize workflows to meet their needs.

The guiding principal behind the v7 architecture is that an extendable codebase is a maintainable codebase. The OpenLMIS-MW implementation has shown that following AngularJS framework standards makes it difficult to extend and maintain logic within the OpenLMIS-UI.

Architecture

Router Moderated Architecture

In a URL-Driven application every screen is directly accessible by a URL. AngularJS and ui.router provide a great start for an application, but the presentation and logic of our implementation is becoming less DRY and brittle - which makes debugging and extension difficult. Maintaining unit test for layout logic has become difficult.

To solve these issues, we are going to use a facade pattern to seperate application logic from application style at the application's main point of entry - the routing logic layer. This facade will be implemented by wrapping function calls to ui-router, which will decouple direct dependencies from OpenLMIS-UI modules to ui-router. By decoupling layout and application logic, the OpenLMIS-UI will implement default behaviors that can be overridden by implementers in ways that are easy to document and test.

Since we are initally wrapping ui.router, we can leave the existing modules and update them to take advantage the wrapped router functionality in an incremental manner. This will also ensure backwards compatibility until the OpenLMIS-UI drops support for AngularJS (which will happen eventually).


Module Types

In the OpenLMIS-UI, we try to keep each module focused on a single function. Single focus modules help keep code organized, and makes it simple to not include sections of functionality by ignoring those files during the OpenLMIS-UI build process.

We expect most of the OpenLMIS-UI modules to be one of these:

  • Data Modules which provide data into the OpenLMIS-UI. Loading, saving, and validating data should happen in these modules.
  • Route Modules define and register an application route or state. These modules should be "skinny".
  • Component Modules provide building blocks and progressive enhancements to the semantic HTML created in the route modules.

Component Tests

Currently the OpenLMIS-UI has guidance for support of unit tests, but doesn't have any testing structure to ensure that a modules desired functionality doesn't accidentally drift. These tests will function much like the current unit testing practices, but focus on testing the entire end-to-end logic created in a module. For data and component modules, component tests won't provide much more value than unit tests.

Component tests for route modules will provide the most value as it will create coverage of the functional logic that is exposed by the UI. These larger integration tests shouldn't be used to test edge cases, but rather larger functional "happy path" scenarios and and possibly common error scenarios.

Data Resources & Domain Objects

Currently the OpenLMIS-UI implements logic that is structured around resourceServices that closely map to OpenLMIS Services. This has lead to repeated code with complicated methods. Javascript objects recieved from an OpenLMIS Service are directly passed to route and component level modules, which makes reasoning about the current behavior of a screen difficult. There are many cases where implementation logic is difficult to follow because of how objects are mutated and passed between services.

This v7 architecture improves the OpenLMIS-UI by implementing patterns to aid code reuse and reasoning. ES6 and functional programming approaches in javascript focus on immutable data, which makes reasoning about the current state of a page easier and is more performant in web browsers. Using ES6's modular syntax will allow the UI to avoid global singletons when implementing business logic, which will help both unit testing and code reuse.

To structure this overall approach, the OpenLMIS-UI will use a paradigm that was popularized with the AngularJS module ngResource. The ngResource pattern allows for a "data resource" to be created as a singleton, which will then return objects that use the domain language to express changes to an object's state.

Data Resources

Data resources provide a layer of abstraction between the OpenLMIS Services and the OpenLMIS-UI. These data resources should always be focused on the domain-level object rather than the OpenLMIS Service that provides data for the object.

An example of where the data resource should cover multiple OpenLMIS Services is the "user" data resource. In the OpenLMIS Services, the "user" domain object has the most information available in the openlmis-referencedata service, but a user's password is updated through the openlmis-auth service.

All data resources will be dependency injected, which will allow other modules to add or modify methods exposed by the data resource. Connecting to a OpenLMIS Service should be considered an incremental improvement to the base factory, and these methods should be added with decorators.

Most data resources will implement a local database so that the resources are available offline, but some resources such as orderables might not have functional requirements to function offline.

Domain Objects

Domain objects implement business logic and interactions with data resources with the same terminology that is used in OpenLMIS functional documentation. This is helpful because unit tests on this domain object can be understood by stakeholders, which helps maintain clear communication between developers and stakeholders. These methods implement business logic and interact with data resources, which helps hide the implementation details of the domain object. Hiding the implementation details of domain objects from controller-level methods results is more maintable code and easier extension by implementers.

To ensure that our domain objects work well in the AngularJS view methodology, all object properties will be directly accessible and mutatble.

The methods for domain objects will either return an immutable value or resolve a promise that returns a new instance of the domain object. Immutable return values help avoid accidental manipulation and accidentally updating the DOM.

All methods on a domain object must not take any arguments, which will aid testing and reasoning about these domain objects.

Usability & Affordance

These are things that improve the OpenLMIS-UI experience, and include directive, components, and CSS. 

Components

Components are popular these days.

Directives

Directives are damn useful for enriching content and workflows.

CSS

Route and data modules should never be allowed to add CSS styles. This could cause issues if one page is supposed to look or act different than another page, but having divergent designs per page makes a UI feel inconsistent (so we should avoid this).


Example Page Load Process

  • No labels