Architect's Guide
to Frontend Frameworks

Tomasz Ducin  •  @tomasz_ducin  •  bit.ly/fe-arch

Hi, I'm Tomasz

Independent Consultant & Software Architect

Trainer, Speaker, JS/TS Expert

ArchitekturaNaFroncie.pl (ANF)

Warsaw, PL

tomasz (at) ducin.dev

@tomasz_ducin

Detecting Changes & Updating the View

     jQuery

jQuery

(resource: freelancer.com contest)

     AngularJS (v1)

Dependency Injection

in AngularJS

angular.module('myMod', ['ngRoute'])

.service('DepartmentsModel', ["$http", "baseURL", function ($http, baseURL) {
  this.getCollection = function (start, end) {
    return $http.get(baseURL + "departments", ...)
  };
}])

.component('userPage', {
  template: require("./templates/user-page.html"),
  controller: function ($scope, UserContext, DepartmentsModel) {
    var ctrl = this;

    ...

    this.fetchDepartments = function () {
      DepartmentsModel.getCollection().then(function (response) {
        ctrl.departments = response.data;
      });
    };
  }
})

Dependency Injection

in AngularJS

angular.module('myMod', ['ngRoute'])

.service('DepartmentsModel', ["$http", "baseURL", function ($http, baseURL) {
  this.getCollection = function (start, end) {
    return $http.get(baseURL + "departments", ...)
  };
}])

.component('userPage', {
  template: require("./templates/user-page.html"),
  controller: function ($scope, UserContext, DepartmentsModel) {
    var ctrl = this;

    ...

    this.fetchDepartments = function () {
      DepartmentsModel.getCollection().then(function (response) {
        ctrl.departments = response.data;
      });
    };
  }
})
<script src="angular.min.js"></script>
<script src="department-service.js"></script>
<script src="user-page-component.js"></script>
...
(another hundreds scripts)

2-way data-binding

in AngularJS

View

Model

<p>First name: {{ firstName }}</p>
// in template
<input ng-model="firstName">
// OR
// in controller
$scope.firstName = "John";

Digest Cycle

in AngularJS

Service

     React

Declarative, State-driven UI Development

export class Heading extends React.Component {
  render(){
    return <div>
      <h1>{ props.heading }</h1>
      <img src={ props.imageURL } />
    </div>
  }
}
export class Heading extends React.Component {
  state = {
    count: 0
  }

  render(){
    return <div>
      <h1>{ props.heading } { this.state.count }</h1>
      <img src={ props.imageURL } />
      <button onClick={this.handleClick}
      <ChildComponent count={this.state.count} />
    </div>
  }

  handleClick = () => {
    this.setState(state => { count: state.count + 1 })
  }
}
export const Heading = (props) =>
  <div>
    <h1>{ props.heading }</h1>
    <img src={ props.imageURL } />
  </div>
export class Heading extends React.Component {
  render(){
    return <div>
      <h1>{ props.heading }</h1>
      <img src={ props.imageURL } />
    </div>
  }
}

Function Component (no this)

Class Component

Component Architecture

smart / container (logic)

dumb / presentational (UI)

passing functions down to the child

DOM manipulations

are very expensive 

DOM

in browsers

<body>
<div>
<div>
<span>
<div>
<img>
<h1>
<p>

Virtual DOM

in React

<body>
<div>
<div>
<span>
<div>
<img>
<h1>
<p>
export const Heading = (props) =>
  <div>
    <h1>{ props.heading }</h1>
    <img src={ props.imageURL } />
  </div>
function Heading(props) {
  return React.createElement("div", null,
    React.createElement("h1", null, props.heading),
    React.createElement("img", {
      src: props.imageURL
    })
  );
};

JSX / TSX

JS / TS

React Diffing Algorithm

render phase (virtual) + commit phase (DOM repaint)

Renders (1-way data-flow)

in React

state change!

state of a parent becomes props (properties) of children

Reactivity in REACT

Avoiding Renders

in React (immutability)

Pure or Memo

(silence)

no renders

same props passed

state change!

export const Heading = (props) =>
  <div>
    <h1>{ props.heading }</h1>
    <img src={ props.imageURL } />
  </div>
export class Heading extends React.Component {
  render(){
    return <div>
      <h1>{ props.heading }</h1>
      <img src={ props.imageURL } />
    </div>
  }
}

Memo - Function Component

Pure - Class Component

export const Heading = React.memo((props) =>
  <div>
    <h1>{ props.heading }</h1>
    <img src={ props.imageURL } />
  </div>)
export class Heading extends React.PureComponent {
  render(){
    return <div>
      <h1>{ props.heading }</h1>
      <img src={ props.imageURL } />
    </div>
  }
}

State Management

private component state or shared

REDUX STORE

dispatch (WRITE)

notify subscribers (READ)

Redux

REDUX STORE

dispatch (WRITE)

notify subscribers (READ)

Redux

  • Pub-Sub

  • Centralized

  • Event Sourcing

REDUX STORE

dispatch (WRITE)

notify subscribers (READ)

Redux

  • Pub-Sub

  • Centralized

  • Event Sourcing

Pub-Subs

  • Pub-Sub

  • Distributed

pub-sub

pub-sub

dispatch (WRITE)

notify subscribers (READ)

Event Bus

  • Pub-Sub

  • Centralized

Event

Bus

     Angular (v2+)

NgModules

  • JS has native module support since ES6
    (import/export keywords)

  • JS has npm - node package manager

  • Angular introduces yet another modularity system: NgModules

NgModules

(!) LOTS OF native JS imports and exports

@NgModule({
  declarations: [
    AppComponent,
    ItemComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],
  exports: [
    ItemComponent
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

local declarations of components, pipes

components, pipes to be available outside

the main view

sometimes you need those :|

import other NgModules

DI vs ES6 modules

export class MyClass { ... }

export const instance = new MyClass()

export const n = 1

export const literal = {
  value: 1
}

export const getLiteral = () => ({
  value: 1
})

export const getLiteralSingleton = singleton(getLiteral)
import { getLiteralSingleton } from './here'

getLiteralSingleton().doThings()
import { Component, EventEmitter, Input, Output } from '@angular/core';
 
@Component({
  selector: 'app-voter',
  template: `
    <h4>{{name}}</h4>
    <button (click)="vote(true)"  [disabled]="voted">Agree</button>
    <button (click)="vote(false)" [disabled]="voted">Disagree</button>
  `
})
export class VoterComponent {
  @Input()  name: string;
  @Output() onVoted = new EventEmitter<boolean>();
  voted = false;
 
  vote(agreed: boolean) {
    this.onVoted.emit(agreed);
    this.voted = true;
  }
}

async task ⚙️

async task ⚙️

async task ⚙️

Zone (zone.js)

Change Detection

in Angular

Dependencies

TypeScript:

Static Type Checking

RxJS:

Reactivity

Reactivity

let rate = 3.94;
let amount = 1000;
let exchange = amount / rate; // 253.80

rate = 3.97;
exchange // DESYNC, sync manually!

Imperative Code

let rate$ = 3.94;
let amount$ = 1000;
let exchange$ = amount$ / rate$; // 253.80

rate$ = 3.97;
exchange$ // 251.89
let rate$ = 3.94;
let amount$ = 1000;
let exchange$ = combineLatest(amount$, rate$,
  (amount, rate) => amount / rate);
let rate$ = 3.94;
let amount$ = 1000;
let exchange$ = combineLatest(amount$, rate$,
  (amount, rate) => amount / rate);

// rate$ == 3.97 ---> exchange$ == 251.89

Reactive Code

Reactive Extensions (RX)
redefine what a variable is

What's next?

     Svelte

Svelte

  • no Virtual DOM

  • true reactivity

<script>
  const price = 9.99
  let quantity = 0;
  $: totalPrice = quantity * price;

  function handleClick() {
    quantity += 1;
  }
</script>

micro-Frontends

Separate Sub-apps

(mockup: https://dribbble.com/shots/6475026-Push-Notifications-Delivered)

host app

(mockup: https://dribbble.com/shots/6475026-Push-Notifications-Delivered)

(mockup: https://dribbble.com/shots/6475026-Push-Notifications-Delivered)

Separate Sub-apps

  • host app

    • message broker

    • bootstraps and kills sub-apps

  • sub-apps

    • iframe-based (window.postMessage)

    • bundled packages (native JS)

    • any framework you like 💜

Micro-Frontends flaws

  • distributed -> more code redundancy

  • loading assets potentially multiple times

  • cross-app communication overhead

📝 Key Takeaways

  • All major frameworks support TypeScript.
    Use it.

  • Redux is powerful, works across frameworks,
    but comes at a big cost.

  • Choose React if FP doesn't scare you.

  • Micro-Frontends help avoid rewriting apps
    and allow independent releases.

Thank you

Tomasz Ducin  •  @tomasz_ducin  •  bit.ly/fe-arch

Architect's Guide to Frontend Frameworks

By Tomasz Ducin

Architect's Guide to Frontend Frameworks

  • 2,744