A Supreme Approach To UI Architecture
Michael K. Snead
"A" Supreme Approach
As part of an agile culture, we change, adapt and evolve and this is just a snapshot.
As told by one member of Team Supreme.
High Level Architecture
App Component
Child
Component
Child
Component
Child
Component
Manager
store.dispatch(action)
Redux
Store/
Dispatcher
Reducer(s)
store.subscribe(...);
state
- Redux "store" has initial state (ie: from server)
- App component subscribes to store
- Children of App receive state from parent via binding
- Child components / Manager dispatch actions
- Redux asks reducers to compute next app state and broadcasts to listener(s) (App)
High Level Architecture
App Component
Child
Component
Child
Component
Child
Component
Manager
store.dispatch(action)
Redux
Store/
Dispatcher
Reducer(s)
store.subscribe(...);
state
UI components only reflect the state passed to them and either dispatch actions directly or ask the Manager to do it.
Logic lives in the Reducers. AJAX API lives in Service(s).
High Level Architecture
Application File Structure
/foo-bar/
/src/
app
manager.es6
reducers.es6
store.es6
actions.es6
validators.es6
/components/
FooList
This serves as the entrypoint for the "foobar" application at runtime, and for the sake of bundling.
This orchestrates asynchronous actions, or actions that must be executed in order.
This contains the reducers for the app. In larger, very complex apps, it might be broken up.
This is the redux store. It depends on the reducers.
This is effectively an enum of all possible actions for this A2 app.
These validators are bundles of validation rules that compute a validation context.
UI Components and their templates for the app live here.
High Level Architecture
Application File Structure
/foo-bar/
/src/
app
manager.es6
reducers.es6
store.es6
actions.es6
validators.es6
/components/
FooList
Action "flow", if needed, lives here.
For AJAX, call out to services that are pass-through to MVC controllers.
Simple apps may not be concerned about action flow or might even exclude the singleton 'Manager'.
Logic for calculating the next state lives here, but no side effects, no asynchronicity. You cannot dispatch from a reducer.
Reducers must be "pure" in a functional sense.
Redux
A functional approach
What are the advertised benefits to a functional approach?
- Taming side effects
- Reducing state changes
- Greater predictability
- Easier extensibility
- Improved testability
- Minimizing moving parts
PluralSight - Functional Programming with C# (less than 1.5 hours for entire course)
Redux
Flux is a pattern of unidirectional data flow, Redux is an implementation.
- Really small (2KB), simple API
- Established
- Framework agnostic
Egghead.io - Getting Started with Redux
After the first 1/2 hour of content you should pretty much know Redux.
Rangle.IO - "Redux is the future of React/A2"
Redux Pros & Cons
- Flux is an established pattern in industry
- Easier to test and maintain long term
- Scales with complexity
- Low risk to buy into / can adopt gradually
Pros
- More boilerplate / planning up front
- New to Paylocity
Cons
Immutable.JS
To get the benefits of FP and maximum speed in React, you want immutability. ImmutableJS by Facebook makes immutability easy.
- Everything in JS is by reference
- Object.assign() is a shallow copy
- ImmutableJS is already proven
- ImmutableJS makes it easier to stay performant
ImmutableJS
Why do we need this?
Egghead.io - Learn How To Use ImmutableJS (subscription required)
ImmutableJS - Roadmap for 2016 with some statistics on its popularity and rating
Building and Testing in CI
- Webpack
- gulp
- node.js 4.2.2 (LTS)
- babel
- karma
- mocha
- phantomjs
Webpack
- Best practices for JavaScript module systems advise bundling all of your packages into one file for production.
In Webpack, this is the case by default. - Filesystem pathing + compile time bundle (vs. URL route pathing) ensures that things like virtual directories can't disrupt your pathing
- Filesystem pathing + compile time bundle makes it simple to use npm/bower modules or files that aren't available offline
- Webpack has optimizations you can enable to make it crazy fast (ie: cache modules). We use compile-on-save and while working with Angular 2 I've seen 800ms-1.6sec without putting much effort into optimization.
- Problems with packaging and dependencies can be caught at compile time (IE: in CI) versus at runtime
Webpack
Most module systems have a similar set of features. Webpack is no exception.
There are plugins to load all sorts of resources, including CSS, images, JSON files, markup, etc.
Webpack also has no problem handling mixed modules (ie: ES7 + TypeScript + CoffeeScript) in the same project.
Webpack provides sourcemaps out of the box.
Webpack also has an API that makes it trivial to use from gulp.
gulp
It's pretty much established that gulp is superior to grunt.js, but why use gulp at all when we have options like npm or webpack?
Since Visual Studio's Task Runner Explorer options are still very limited, even if we use it for nothing else, we can use gulp to make it easy to plug in task-based processes to Visual Studio. gulp acts as the bridge between the two.
This enables us to start/run tasks on solution open, or on build/rebuild.
node.js 4.2.2 (LTS)
node.js recently had their first LTS (Long Term Support) release. This means a stable API and longer-term bug/security fix support.
We bundle all of our binaries in source control and execute them by overriding the PATH variable for the process/session in TeamCity. We also configure VS to use the installed node.js, versus the old one it comes packaged with.
We also package the node modules needed to build/test and unpack them using 7zip to avoid network-related errors/slowness.
npm 3.5.2
HTTP
Among the great contenders for AJAX there are...
- Axios
Axios is popular/battle tested, used by Team Awesome, resembles future fetch APIs. - jQuery AJAX
It's likely jQuery will always be on our pages as it's a requirement for Kendo. We already know jQuery. -
fetch
The standard of tomorrow through polyfill. Chrome + Firefox have already implemented fetch API.
This is what we've chosen to use in new pages.
We specify application/json in headers and add a new attribute to validate anti-forgery token in one of those headers.
ES2015 / ES7
We use WebStorm (or possibly VSCode) for an "IDE feel" environment.
We have Babel configured such that stage-0 ES7 options are turned on.
Questions?
Thanks for your time.
Supreme Approach to UI Architecture
By Michael Snead
Supreme Approach to UI Architecture
Illustrate Supreme's current approach to UI Architecture, as told by Snead.
- 2,411