Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Components Are the New Thin Client

554 views

Published on

One of the biggest obstacles to building non-trivial applications today is state management. Even some of the best architects struggle to keep state consistent throughout an application. However, by taking cues from classical physics, we can build an app that has a consistent state and, as a result, is predictable. This allows us to not only predict what will happen in the future of our app state but also pull back the curtain and see what happened in the past of our app state.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Components Are the New Thin Client

  1. 1. components are the new THIN CLIENT!
  2. 2. Story Time
  3. 3. DOM jQuery “Application” $( "form" ).submit(function( event ) { if ( $( "input:first" ).val() === "correct" ) { $( "span" ).text( "Validated..." ).show(); return; } $( "span" ).text( "Not valid!" ).show().fadeOut( 1000 ); event.preventDefault(); });
  4. 4. DOM First Generation AngularJS Application CONTROLLER
  5. 5. DOM Second Generation AngularJS Application CONTROLLER SERVICE DOM CONTROLLER
  6. 6. Typical Stateful Service
  7. 7. Enter Redux STOREREDUCERS
  8. 8. In classical physics, if you know everything about a system at some instant of time, and you also know the equations that govern how the system changes then you can predict the future. That's what we mean when we say that the classical laws of physics are deterministic. If we can say the same thing, but with the past and future reversed, the same equations tell you everything about the past. Such a system is called reversible. The Theoretical Minimum by Leonard Susskind
  9. 9. In classical physics, if you know everything about a system at some instant of time, and you also know the equations that govern how the system changes then you can predict the future. That's what we mean when we say that the classical laws of physics are deterministic. If we can say the same thing, but with the past and future reversed, the same equations tell you everything about the past. Such a system is called reversible. The Theoretical Minimum by Leonard Susskind
  10. 10. STORE!
  11. 11. In classical physics, if you know everything about a system at some instant of time, and you also know the equations that govern how the system changes then you can predict the future. That's what we mean when we say that the classical laws of physics are deterministic. If we can say the same thing, but with the past and future reversed, the same equations tell you everything about the past. Such a system is called reversible. The Theoretical Minimum by Leonard Susskind
  12. 12. REDUCERS!
  13. 13. In classical physics, if you know everything about a system at some instant of time, and you also know the equations that govern how the system changes then you can predict the future. That's what we mean when we say that the classical laws of physics are deterministic. If we can say the same thing, but with the past and future reversed, the same equations tell you everything about the past. Such a system is called reversible. The Theoretical Minimum by Leonard Susskind
  14. 14. In classical physics, if you know everything about a system at some instant of time, and you also know the equations that govern how the system changes then you can predict the future. That's what we mean when we say that the classical laws of physics are deterministic. If we can say the same thing, but with the past and future reversed, the same equations tell you everything about the past. Such a system is called reversible. The Theoretical Minimum by Leonard Susskind
  15. 15. User Input and Response
  16. 16. Time and Space
  17. 17. ACTIONS!
  18. 18. Space: Manual Dispatch
  19. 19. CLICK DISPATCH
  20. 20. actions = [ {type: '[Client] Select', payload: {id: '1', firstName: 'John', lastName: 'Doe', company: 'Acme, Inc'}}, {type: '[Client] Select', payload: {id: '2', firstName: 'Jane', lastName: 'Smith', company: 'Super, Inc'}}, {type: '[Client] Select', payload: {id: '1', firstName: 'John', lastName: 'Doe', company: 'Acme, Inc'}}, {type: '[Client] Select', payload: {id: '2', firstName: 'Jane', lastName: 'Smith', company: 'Super, Inc'}}, {type: '[Client] Select', payload: {id: '1', firstName: 'John', lastName: 'Doe', company: 'Acme, Inc'}}, {type: '[Client] Select', payload: {id: '2', firstName: 'Jane', lastName: 'Smith', company: 'Super, Inc'}}, ]; index = 0; step() { this.index = this.index < this.actions.length - 1 ? this.index + 1 : 0; this.store.dispatch(this.actions[this.index]); } Manual Dispatch
  21. 21. DEMO TIME!
  22. 22. Time: Manual Cycle
  23. 23. CLICK DISPATCH
  24. 24. actions = [ {type: '[Client] Select', payload: {id: '1', firstName: 'John', lastName: 'Doe', company: 'Acme, Inc'}}, {type: '[Client] Select', payload: {id: '2', firstName: 'Jane', lastName: 'Smith', company: 'Super, Inc'}}, {type: '[Client] Select', payload: {id: '1', firstName: 'John', lastName: 'Doe', company: 'Acme, Inc'}}, {type: '[Client] Select', payload: {id: '2', firstName: 'Jane', lastName: 'Smith', company: 'Super, Inc'}}, {type: '[Client] Select', payload: {id: '1', firstName: 'John', lastName: 'Doe', company: 'Acme, Inc'}}, {type: '[Client] Select', payload: {id: '2', firstName: 'Jane', lastName: 'Smith', company: 'Super, Inc'}}, ]; cycle() { const result = Observable .from(this.actions) .zip(Observable.interval(this.timerInterval), (a, b) => a) ; result.subscribe(action => this.store.dispatch(action)); } Manual Cycle
  25. 25. DEMO TIME!
  26. 26. Space: Dynamic Dispatch
  27. 27. SUBMIT CLICK DISPATCH
  28. 28. fetchSingle() { this.actionsService .single() .subscribe(action => this.action = JSON.stringify(action, null , 't')); } dispatch(action) { this.store.dispatch(JSON.parse(action)); } Dynamic Dispatch
  29. 29. DEMO TIME!
  30. 30. Time: Dynamic Cycle
  31. 31. SUBMIT CLICK DISPATCH
  32. 32. fetchAll() { this.actionsService .all() .subscribe(actions => this.rawActions = JSON.stringify(actions, null , 't')); } dispatchCycle(rawActions) { const actions = JSON.parse(rawActions); const result = Observable .from(actions) .zip(Observable.interval(this.timerInterval), (a, b) => a) ; result.subscribe((action: any) => this.store.dispatch(action)); } Dynamic Dispatch
  33. 33. DEMO TIME!
  34. 34. Space: Remote Dispatch
  35. 35. constructor( private actionsService: ActionsService, private store: Store<reducers.AppState>, private afs: AngularFirestore ) { this.remoteActions = afs.collection('actions'); } ngOnInit() { this.remoteActions.valueChanges() .skip(1) .subscribe((actions: any) => { this.store.dispatch(actions[0]); }); } dispatchRemote(action) { this.remoteActions.add(JSON.parse(action)); } Remote Dispatch
  36. 36. DEMO TIME!
  37. 37. Time: Undo and Redo
  38. 38. export function undoable(reducer: ActionReducer<any>): ActionReducer<any> { // Call the reducer with empty action to populate the initial state const initialState = { past: [], present: reducer(undefined, { type: ''}), future: [] }; // Return a reducer that handles undo and redo return function (state = initialState, action) { const { past, present, future } = state; switch (action.type) { case 'UNDO': // UNDO LOGIC case 'REDO': // REDO LOGIC default: // REGULAR LOGIC } }; } Meta Reducer
  39. 39. DEMO TIME!
  40. 40. REAL WORLD APPLICATIONS
  41. 41. Redux Airbrake https://www.npmjs.com/package/redux-airbrake @castillo__io BONUS SLIDE!
  42. 42. Space and Time: Thin Component
  43. 43. const CLIENT_LOAD = '[Client] Load'; const CLIENT_CREATE = '[Client] Create'; const CLIENT_UPDATE = '[Client] Update'; const CLIENT_DELETE = '[Client] Delete'; const CLIENT_SELECT = '[Client] Select'; const CLIENT_CLEAR = '[Client] Clear'; Actions
  44. 44. const reducer = (state = initialState, {type, payload}) => { switch (type) { case CLIENT_LOAD: return state; case CLIENT_SELECT: return selectClient(state, payload); case CLIENT_CREATE: return createClient(state, payload); case CLIENT_UPDATE: return updateClient(state, payload); case CLIENT_DELETE: return deleteClient(state, payload); case CLIENT_CLEAR: return clearClient(state, payload); default: return state; } }; const store = new Store(reducer, initialState); Reducer
  45. 45. export class ClientsComponent implements OnInit { clients$: Observable<Client[]>; currentClient$: Observable<Client>; constructor(private store: Store) { this.clients$ = this.store.select('clients'); this.currentClient$ = this.store.select('currentClient'); } ngOnInit() { this.store.dispatch(new ClientActions.LoadAction()); this.resetCurrentClient(); } resetCurrentClient() { const newClient: Client = { id: null, firstName: '', lastName: '', company: '' }; this.store.dispatch(new ClientActions.SelectAction(newClient)); } } ClientsComponent
  46. 46. class Store { constructor(reducer) { this.reducer = reducer; this.state = reducer(undefined, { type: ''}); } getState() { return this.state.present; } dispatch(action) { this.state = this.reducer(this.state, action); } } Store
  47. 47. io.on('connection', (socket) => { console.log('user connected'); io.emit('update', store.getState()); socket.on('dispatch', action => { store.dispatch(action); io.emit('update', store.getState()); }); socket.on('disconnect', function () { console.log('user disconnected'); }); }); http.listen(5000, () => { console.log('started on port 5000'); }); Socket.io
  48. 48. const BASE_URL = 'http://localhost:5000'; export class SocketService { private socket; private subjects = {}; constructor() { this.socket = io(BASE_URL); this.socket.on('update', data => this.next(data)); } select(key) { this.subjects[key] = new BehaviorSubject(null); return this.subjects[key].asObservable(); } next(data) { for (const key in this.subjects) { if (this.subjects.hasOwnProperty(key)) { this.subjects[key].next(data[key]); } } } dispatch(action) { this.socket.emit('dispatch', action); } } SocketService
  49. 49. DEMO TIME!
  50. 50. https://github.com/onehungrymind/component-thin-clients
  51. 51. @simpulton
  52. 52. https://venturplex.com/
  53. 53. WE❤YOU!
  54. 54. Thanks!

×