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
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:
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.
→ See the current build process documentation
→ Keep it simple, semantic, and shallow — fancy things are hard, and avoid complex designs → I'll add more here later
In the OpenLMIS-UI we consider a module a directory that contains a single feature. There are three buckets of modules:
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.
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 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.
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.
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~
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
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:
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.
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.
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
Whoops -- I dropped a section of this message, so I'm reposting for clarity
In the OpenLMIS-UI we consider a module a directory that contains a single feature. There are three buckets of modules:
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.
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
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
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.
--- 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
To post to this group, send email to openl...@googlegroups.com.