Updating OpenLMIS-UI Architecture

16 views
Skip to first unread message

Nick Reid

unread,
Sep 28, 2017, 3:35:01 PM9/28/17
to Openlmis Dev Mailing List, Nikodem Graczewski

Dear dev-


I've stated another document about how to improve our OpenLMIS-UI architecture. This is coming from a need to make the codebase more DRY and more maintainable — as Nikodem Graczewski and I have been discussing coding practices that are inconsistent at best


I don't want to be the sole dictator of the OpenLMIS-UI, since we all work on this together. Consider this document a high-level framework independent discussion. I'd love feedback or general thoughts on:
  • The goals for the application (ie the overview)
  • The moderator
  • The eventService

Please comment on the OpenLMIS wiki, or respond to this email — most of this won't turn into immediate work, but I plan on introducing really simple high-level services once we get some consensus

Wiki page:

UI Overview

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


We aim to support "the last mile" which means we do our best to:

  • OfflineFirst
  • Minimize HTTP Requests
  • Display meaningful application state

That said, these are goals and not mandates — we all have deadlines and do our best to support the communities that use OpenLMIS — get people the tools they need first, and make it better second.

Build Process

→ See the current build process documentation

HTML & CSS

→ Keep it simple, semantic, and shallow — fancy things are hard, and avoid complex designs → I'll add more here later

Javascript Application Architecture

Modules

In the OpenLMIS-UI we consider a module a directory that contains a single feature. There are three buckets of modules:

  • Model Modules which bring data into the application, store application state, and/or process data
    • I expect this bucket to be most of our application code
  • View Modules which are CSS styles, UI Components, and other pieces that make views simpler
  • Controller Modules which are controllers, HTML, and configuration to make a screen show up
    • These should be skinny and ideally look very simple to an implementer

Note: I wanted to call these things Data, View, and Pretty modules - but making up new terms is usually a bad idea

No module should ever fit in more than one of these buckets. 

Moderator: The routeProvider

Since the OpenLMIS-UI is URL-Driven, every screen and modal should be directly accessible by a URL. AngularJS and ui.router provide a great start for an application, but we are hitting the limits of what we can do with this initial framework without it turning into a writing nightmare that is hard to document and test.

The routeProvider is a facade pattern that will essentially wrap ui-router. This will help us divide layout issues from application logic, and provides logical places for extension.

Routes will be created with configuration objects with "strong defaults", not specialized methods, so implementers can drop or change functionality without needing to worry about breaking the application architecture.

We are using the name "routeProvider" because other libraries are nice enough to leave us this namespace — since all application logic should point here, it should have the most obvious name.

NOTE: Using ui.router and existing conventions is fine for now, we will be moving towards a routeProvider based infrastructure and clean up existing ui.router code ... eventually.

Events: eventService

Events are great for decoupling modules and providing extension points. AngularJS has an event system built in — but we have different patterns of using it and are not even using all of the features provided.

We will be adding an eventService which will only pass events at the top-level of our application architecture. Technically, this means we are only exposing $rootScope.$emit and $rootScope.$on — with slightly different names. Only model modules should be aware of the event service.

NOTE: UI Components should also use events, but since they rely on the AngularJS architecture, they will use $rootScope for communication. I'll try to add specifics for UI Components when we revise the conventions and documentation for them.

Main Application Data Services

These are the primary services that are used through the OpenLMIS-UI application. The services could be put behind the routeProvider facade, but since they are primary, its best if other modules depend on them directly.

  • authorizationService keeps track of the current user, and what rights/permissions that user has
  • messageService handles internationalizing strings. Behind this service are other services that maintain the current state and other internationaliztion preferences
  • offlineService keeps track of if the OpenLMIS-UI is currently offline, and will emit events as that state changes
  • openlmisUrlService keeps track of where the OpenLMIS services live.
    • I'd like feedback if this should be extended so it actually does HTTP requests, which would make some markup cleaner
  • loadingService keeps track of if the UI is loading data, so url state transitions can be orchestrated
    • This could be hidden inside the routeProvider — I'd like feedback on this


Nick Reid | nick...@villagereach.org
Software Developer, Information Systems Group


VillageReach Starting at the Last Mile
2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA
CELL: +1.510.410.0020
SKYPE: nickdotreid
www.villagereach.org


Nikodem Graczewski

unread,
Sep 29, 2017, 3:47:55 AM9/29/17
to OpenLMIS Dev
Hi Nick,

I'm not sure I understand the purpose of the routeProvider (and how it would work). Do you mean the AngularJS $routeProvider or some provider that would be created by us? If we would implement it, should it really be called routeProvider, as I can already see people confusing it with $routeProvider, I actually did at first. Could you give us some more details?

Also, why do we want to wrap AngularJS event handling? Wouldn't it be easier for the implementers to use the event handling they know from AngularJS and give them more freedom on what they want to do?

Regards,
Nikodem

Nick Reid

unread,
Oct 2, 2017, 11:05:52 AM10/2/17
to Nikodem Graczewski, OpenLMIS Dev

Nikodem~


To rephrase your questions (for clarity, and to enumerate them so this thread is easier to follow):

(A) What is the routeProvide's role?

---> (A1) Naming

(B) Why restrict registering and sending events?

(C) Why wrap AngularJS?



(A) What is the routeProvide's role?

The idea behind the facade pattern is to provide a unified interface for objects, which should help decrease tight-coupling and ideally increase documentation. Personally, I think of it like setting up a TV — you put the wires in the back, so guests just see a single box.


Right now we have layout and state methods in our route configuration — where the route configuration should really be requesting changes, not making changes.


Creating a small wrapper will also allow for clear extension points and mechanisms, that will be easier to document and unit test.


I'm really pulling all these ideas from two sources:

(1) Addy Osmani's Javascript Patterns (Facade section)

(2) Micha Godbolt's Frontend Architecture for Design Systems

---> I recommend spending time with both


(A1) Naming

I'll admit I just took the most obvious name. 


I'm happy to make the name more verbose: openlmisRouteProvider

or since we should really be registering states, not URLs: openlmisStateProvider


(B) Why restrict registering and sending events?

AngularJS's event system is made to send events across scopes in all sorts of different directions. This can cause performance issues, but more importantly allows for logic to change between components. In the next 6 months, there will be multiple teams working on the same platform — and I can't review every team's work to ensure logical consistency.


Architecture ensures consistency. A form of architecture is making objects simpler — because most frameworks are not architecture. AngularJS does a good job at keeping logic separated, but doesn't establish a formal architecture (unlike React).


(C) Why wrap AngularJS?

This is more defensive than anything else — in the next few years — the OpenLMIS-UI will need to move off of AngularJS v1, and I don't trust Angular v2 (now v4) as that community seems to have a lack of cohesion — devs are splitting to other frameworks.


Adding small and opinionated wrappers to objects will make migrating easier. AngularJS is not super opinionated framework, so logic can be expressed in many different ways, which makes changing frameworks an unpredictable mess.


Less options means more consistent code, which means more maintainable code, which means we will be OK when AngularJS stops being maintained (as Google doesn't use any form of Angular in any of their products)


Helpful?

---- nick ---


Nick Reid | nick...@villagereach.org
Software Developer, Information Systems Group


VillageReach Starting at the Last Mile
2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA
CELL: +1.510.410.0020
SKYPE: nickdotreid
www.villagereach.org



From: openlm...@googlegroups.com <openlm...@googlegroups.com> on behalf of Nikodem Graczewski <ngrac...@soldevelo.com>
Sent: Friday, September 29, 2017 12:47:55 AM
To: OpenLMIS Dev
Subject: [openlmis-dev] Re: Updating OpenLMIS-UI Architecture
 

SolDevelo
Sp. z o.o. [LLC] / www.soldevelo.com
Al. Zwycięstwa 96/98, 81-451, Gdynia, Poland
Phone: +48 58 782 45 40 / Fax: +48 58 782 45 41

--
You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev...@googlegroups.com.
To post to this group, send email to openlm...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/openlmis-dev/df63e51b-fac0-47aa-96b4-39d66eee0a5e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Nick Reid

unread,
Oct 2, 2017, 11:55:06 AM10/2/17
to OpenLMIS Dev, Ben Leibert

Ben had comments on this document, I'm sending them to the dev list so we are transparent


From the document:

We aim to support "the last mile" which means we do our best to:

    • OfflineFirst
    • Minimize HTTP Requests
    • Display meaningful application state

From Ben:

What does this mean precisely, and how/why is it related to aiming to support "the last mile?"


The thought here is that when working with a slow/unstable network, or with people that have huge varities of experience with technology — its better to be verbose.
-- Loading screens should explain what they are loading
-- Error message should be specific

-- If a person must take an action (update, submit, ect) then it should be obvious.



From the document:

From Ben:

To me, at least, this is confusing. You define a module as “a directory that contains a single feature.” You also describe M, V, and C as “three buckets of modules, ” which I interpret as “three types of modules.” I imagine, however, that many features involve more than just an M, a V, or a controller. I imagine that many need all three, which means a single feature may be associated with multiple types of modules. 


Yes, a single feature or story ticket could include multiple modules. As an example, I'm going to refer to OLMIS-396 "Upload ISA Values" 
PS: I don't mean to make recommendations to whom ever works on OLMIS-396, this is just a thought exercise

In the UI there should be:
(a) A controller module that renders the page where the upload takes place
(b) A model module that actually uploads the file to the correct OpenLMIS Service endpoint
(c) and possibly a View module that displays a stylized file-upload input

(b) and (c) could be reusable, where as (a) shows a very specific screen at a specific state in the OpenLMIS-UI application. (a) might create a simple factory that wraps (b), allowing (b) to be a resource that sends CSV files to the OpenLMIS Service (and maybe supports offline uploading and pre-parsing of files). The functionality that (a) brings might need to be included in an existing module.

We have been making tickets that are vertically integrated, which means a single feature includes work in the backend and frontend of the application — which should be designed to promote reuse (and not be a SOAP-y POS)

I am concerned that we might make so many modules that it becomes impossible for a developer to figure out which module to make changes in. The referencedata-ui repository has more than 20 modules in it. Personally, I don't think we are close to that yet.

I'm curious if other people have this same confusion, and if I should pick out different terms than MVC so there is less confusion.

-- nick ---


Nick Reid | nick...@villagereach.org
Software Developer, Information Systems Group


VillageReach Starting at the Last Mile
2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA
CELL: +1.510.410.0020
SKYPE: nickdotreid
www.villagereach.org



From: openlm...@googlegroups.com <openlm...@googlegroups.com> on behalf of Nick Reid <nick...@villagereach.org>
Sent: Monday, October 2, 2017 8:05:48 AM
To: Nikodem Graczewski; OpenLMIS Dev
Subject: Re: [openlmis-dev] Re: Updating OpenLMIS-UI Architecture
 

Nick Reid

unread,
Oct 2, 2017, 2:14:11 PM10/2/17
to OpenLMIS Dev, Ben Leibert

Whoops -- I dropped a section of this message, so I'm reposting for clarity


Ben had comments on this document, I'm sending them to the dev list so we are transparent

From the document:
We aim to support "the last mile" which means we do our best to:
    • OfflineFirst
    • Minimize HTTP Requests
    • Display meaningful application state

From Ben:
 
What does this mean precisely, and how/why is it related to aiming to support "the last mile?"

The thought here is that when working with a slow/unstable network, or with people that have huge varieties of experience with technology — its better to be verbose.

-- Loading screens should explain what they are loading
-- Error message should be specific
-- If a person must take an action (update, submit, ect) then it should be obvious.


From the document:

In the OpenLMIS-UI we consider a module a directory that contains a single feature. There are three buckets of modules:

  • Model Modules which bring data into the application, store application state, and/or process data
    • I expect this bucket to be most of our application code
  • View Modules which are CSS styles, UI Components, and other pieces that make views simpler
  • Controller Modules which are controllers, HTML, and configuration to make a screen show up
    • These should be skinny and ideally look very simple to an implementer

Note: I wanted to call these things Data, View, and Pretty modules - but making up new terms is usually a bad idea


No module should ever fit in more than one of these buckets. 

From Ben:
To me, at least, this is confusing. You define a module as “a directory that contains a single feature.” You also describe M, V, and C as “three buckets of modules, ” which I interpret as “three types of modules.” I imagine, however, that many features involve more than just an M, a V, or a controller. I imagine that many need all three, which means a single feature may be associated with multiple types of modules. 

Yes, a single feature or story ticket could include multiple modules. As an example, I'm going to refer to OLMIS-396 "Upload ISA Values" 
PS: I don't mean to make recommendations to whom ever works on OLMIS-396, this is just a thought exercise

In the UI there should be:
(a) A controller module that renders the page where the upload takes place
(b) A model module that actually uploads the file to the correct OpenLMIS Service endpoint
(c) and possibly a View module that displays a stylized file-upload input

(b) and (c) could be reusable, where as (a) shows a very specific screen at a specific state in the OpenLMIS-UI application. (a) might create a simple factory that wraps (b), allowing (b) to be a resource that sends CSV files to the OpenLMIS Service (and maybe supports offline uploading and pre-parsing of files). The functionality that (a) brings might need to be included in an existing module.

We have been making tickets that are vertically integrated, which means a single feature includes work in the backend and frontend of the application — which should be designed to promote reuse (and not be a SOAP-y POS)

I am concerned that we might make so many modules that it becomes impossible for a developer to figure out which module to make changes in. The referencedata-ui repository has more than 20 modules in it. Personally, I don't think we are close to that yet.

I'm curious if other people have this same confusion, and if I should pick out different terms than MVC so there is less confusion.


Nick Reid | nick...@villagereach.org
Software Developer, Information Systems Group


VillageReach Starting at the Last Mile
2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA
CELL: +1.510.410.0020
SKYPE: nickdotreid
www.villagereach.org


Sent: Monday, October 2, 2017 8:55:01 AM
To: OpenLMIS Dev
Cc: Ben Leibert

Nikodem Graczewski

unread,
Oct 3, 2017, 3:35:19 AM10/3/17
to OpenLMIS Dev
Hi Nick,

does this mean that we are making preparation for jumping off the Angular train?

Also I'm not sure I understand the below.

In the UI there should be:
(a) A controller module that renders the page where the upload takes place
(b) A model module that actually uploads the file to the correct OpenLMIS Service endpoint
(c) and possibly a View module that displays a stylized file-upload input
 
Does this mean that we should place controller, factory and html in separate modules? I'm getting a little confused. 

Best regards,
Nikodem

On Thursday, September 28, 2017 at 9:35:01 PM UTC+2, Nick Reid wrote:

Nick Reid

unread,
Oct 3, 2017, 9:55:03 AM10/3/17
to Nikodem Graczewski, OpenLMIS Dev

Is the OpenLMIS-UI jumping off AngularJS v1?

yes, but probably in 1-2 years — it's a big unknown about what will happen to the framework, so we should be doing our best to prevent a full rewrite.


Code Organization

Does this mean that we should place controller, factory and html in separate modules? I'm getting a little confused. 


No, and this might be why I should choose some different names. I've noticed we have 3 "buckets" of work, all of which go into different modules — yes, we are already doing this

View Configuration (I called this "Controller")
Example: Making an admin screen for users
Includes: router configuration, html, controller,js files, and maybe small data factories for use in the router
Excludes: CSS, Services

Data Resources (I called this "Model")
Example: Making a AngularJS service that communicates with the OpenLMIS users endpoint
Includes: services, factories, plain old javascript objects (POJOs)
Excludes: HTML, router configuration, controllers

Pretty Stuff (called this "Views") — taking suggestions for better names
Example: User icon display component
Includes: CSS, AngularJS Directives, Components, controllers, HTML
Excludes: Routes, Services, Factories


Is this more clear?


--- nick --


Nick Reid | nick...@villagereach.org
Software Developer, Information Systems Group


VillageReach Starting at the Last Mile
2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA
CELL: +1.510.410.0020
SKYPE: nickdotreid
www.villagereach.org


Sent: Tuesday, October 3, 2017 12:35:19 AM

To: OpenLMIS Dev
Subject: [openlmis-dev] Re: Updating OpenLMIS-UI Architecture

SolDevelo
Sp. z o.o. [LLC] / www.soldevelo.com
Al. Zwycięstwa 96/98, 81-451, Gdynia, Poland
Phone: +48 58 782 45 40 / Fax: +48 58 782 45 41

--
You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev...@googlegroups.com.
To post to this group, send email to openlm...@googlegroups.com.

Nikodem Graczewski

unread,
Oct 4, 2017, 3:26:52 AM10/4/17
to OpenLMIS Dev
Hi Nick,

it is, thank you! :)

Best regards,
Nikodem
To post to this group, send email to openl...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages