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.