The Art of Front-end Architecture

A presentation at JSWorld in February 2021 in by Adrià Fontcuberta

Slide 1

Slide 1

The Art(?) of Front-end Architecture

Slide 2

Slide 2

👋 Hi! I’m Adrià Fontcuberta Senior(?) Software Engineer @ Holaluz Member of the official Vue Test Utils Member of Testing Library Co-organizer of VueJS Barcelona @afontq afontcu.dev

Slide 3

Slide 3

What are* we aiming for?

Slide 4

Slide 4

What are* we aiming for? Write maintainable, scalable apps

Slide 5

Slide 5

What are* we aiming for? Write maintainable, scalable apps Stay away from the framework

Slide 6

Slide 6

What are* we aiming for? Write maintainable, scalable apps Share knowledge between teams and areas Stay away from the framework

Slide 7

Slide 7

What are* we aiming for? Write maintainable, scalable apps Stay away from the framework Share knowledge between teams and areas Reduce the gap between Front and Back

Slide 8

Slide 8

This is not about writing good software, but how to build software that can change over time

Slide 9

Slide 9

Slide 10

Slide 10

Slide 11

Slide 11

Slide 12

Slide 12

Slide 13

Slide 13

Slide 14

Slide 14

Slide 15

Slide 15

Slide 16

Slide 16

Slide 17

Slide 17

Slide 18

Slide 18

Slide 19

Slide 19

Slide 20

Slide 20

Business stuff Delivery stuff

Slide 21

Slide 21

Slide 22

Slide 22

Business stuff Delivery stuff

Slide 23

Slide 23

Business stuff Delivery stuff

Slide 24

Slide 24

Business stuff Delivery stuff

Slide 25

Slide 25

Delivery stuff

Slide 26

Slide 26

Domain Use cases Delivery stuff

Slide 27

Slide 27

Slide 28

Slide 28

Domain

Slide 29

Slide 29

Domain Use cases

Slide 30

Slide 30

Domain Use cases Delivery

Slide 31

Slide 31

Slide 32

Slide 32

This layer has two streams of data

Slide 33

Slide 33

Slide 34

Slide 34

Slide 35

Slide 35

Infrastructure

Slide 36

Slide 36

Infrastructure UI / Input

Slide 37

Slide 37

Infrastructure API repositories Cookies Web Storage

Slide 38

Slide 38

UI ¯_(ツ)_/¯

Slide 39

Slide 39

What does layer really mean?

Slide 40

Slide 40

Slide 41

Slide 41

Dependencies only point inwards

Slide 42

Slide 42

Dependencies only point inwards An inner layer should never rely on anything from an outer layer.

Slide 43

Slide 43

Dependencies only point inwards An inner layer should never rely on anything from an outer layer.

Slide 44

Slide 44

Slide 45

Slide 45

Use case “Get user information” depends on “User”

Slide 46

Slide 46

Use case “Get user information” depends on “User”

Slide 47

Slide 47

Use case “Get user information” depends on “User”

Slide 48

Slide 48

Use case “Get user information” depends on “User” Use case “Get user information” depends on the UI framework

Slide 49

Slide 49

Use case “Get user information” depends on “User” Use case “Get user information” depends on the UI framework

Slide 50

Slide 50

Use case “Get user information” depends on “User” Use case “Get user information” depends on the UI framework

Slide 51

Slide 51

Slide 52

Slide 52

The Web is a delivery mechanism

Slide 53

Slide 53

It is not the center. It is external.

Slide 54

Slide 54

It is really, really hard to get it right

Slide 55

Slide 55

We need to adapt its content for our inner layers

Slide 56

Slide 56

We need to adapt its content for our inner layers

Slide 57

Slide 57

Domain Use cases Infrastructure Adapter UI

Slide 58

Slide 58

Domain Use cases Infrastructure Adapter UI

Slide 59

Slide 59

Domain Use cases Infrastructure Adapter UI Independent of the framework

Slide 60

Slide 60

Domain Use cases Independent of the framework Infrastructure Adapter UI The framework™

Slide 61

Slide 61

Domain Use cases Infrastructure Independent of the framework

Slide 62

Slide 62

Domain Use cases Infrastructure Independent of the framework

Slide 63

Slide 63

Slide 64

Slide 64

Slide 65

Slide 65

Slide 66

Slide 66

infra UI adapter app* domain

Slide 67

Slide 67

infra 👩 UI adapter app* domain

Slide 68

Slide 68

Actions Reducers View State Actions Mutations View State

Slide 69

Slide 69

infra 👩 UI adapter app domain

Slide 70

Slide 70

infra state 👩 UI mutations actions app domain

Slide 71

Slide 71

type Coordinate = 0 | 1 | 2 type Sign = “X” | “O” | “” class Board { public isFull(): boolean {} public isPositionTaken(cell: Cell): boolean {} public fillPosition(cell: Cell, player: Player): void {} } class Cell { private row: Coordinate private col: Coordinate } class Player { public sign: Sign public equals(player: Player): boolean {} }

Slide 72

Slide 72

type Coordinate = 0 | 1 | 2 type Sign = “X” | “O” | “” class Board { public isFull(): boolean {} public isPositionTaken(cell: Cell): boolean {} public fillPosition(cell: Cell, player: Player): void {} } T O N S I S I TH ” Y A W E T I R E H “T class Cell { private row: Coordinate private col: Coordinate } class Player { public sign: Sign public equals(player: Player): boolean {} }

Slide 73

Slide 73

type Coordinate = 0 | 1 | 2 type Sign = “X” | “O” | “” class Board { public isFull(): boolean {} public isPositionTaken(cell: Cell): boolean {} public fillPosition(cell: Cell, player: Player): void {} } class Cell { private row: Coordinate private col: Coordinate } class Player { public sign: Sign public equals(player: Player): boolean {} }

Slide 74

Slide 74

class Game { private board: Board private isEnded: (): boolean private getLastPlayer(): Player public makeMove(player: Player, cell: Cell): void { if (this.isEnded()) throw new FinishedGameException() if (player.equals(this.getLastPlayer())) throw new AlreadyPlayedException() if (this.board.isPositionTaken(cell)) throw new AlreadyTakenException() this.board.fillPosition(cell, player) } }

Slide 75

Slide 75

function makeMoveUseCase({ game, player, cell }, { onSuccess, onError }) { try { game.makeMove(player, cell) } catch (error) { onError(error) return } onSuccess(player, cell) }

Slide 76

Slide 76

function makeMoveUseCase({ game, player, cell }, { onSuccess, onError }) { try { game.makeMove(player, cell) } catch (error) { onError(error) return } onSuccess(player, cell) }

Slide 77

Slide 77

function makeMoveUseCase({ game, player, cell }, { onSuccess, onError }) { try { game.makeMove(player, cell) } catch (error) { onError(error) return } onSuccess(player, cell) }

Slide 78

Slide 78

import { makeMoveUseCase } from ‘application/!!…’ const actions = { makeMove({ state, commit }, cell) { commit(‘MAKE_MOVE_REQUEST’) makeMoveUseCase({ player: state.currentPlayer, game: state.game, cell }, { onSuccess: (player) !=> commit(‘MAKE_MOVE_SUCCESS’, player), onError: (error) !=> commit(‘MAKE_MOVE_ERROR’, error.message) }) } }

Slide 79

Slide 79

import { makeMoveUseCase } from ‘application/!!…’ const actions = { makeMove({ state, commit }, cell) { commit(‘MAKE_MOVE_REQUEST’) makeMoveUseCase({ player: state.currentPlayer, game: state.game, cell }, { onSuccess: (player) !=> commit(‘MAKE_MOVE_SUCCESS’, player), onError: (error) !=> commit(‘MAKE_MOVE_ERROR’, error.message) }) } }

Slide 80

Slide 80

import { makeMoveUseCase } from ‘application/!!…’ $(‘#submit_move’).click(function() { makeMoveUseCase( { player: $(‘#current_player’).val(), game: window.game, cell: [$(‘input[name=”row”]’).val(), $(‘input[name=”col”]’).val()] }, { onSuccess: () !=> $(‘#success_message’).fadeIn(‘slow’), onError: () !=> window.alert(‘oops something went wrong’) } ) })

Slide 81

Slide 81

infra UI adapter app domain

Slide 82

Slide 82

Disclaimer #1 You might not need any of this

Slide 83

Slide 83

Disclaimer #1 You might want some parts of it

Slide 84

Slide 84

Disclaimer #2 This talk was actually about the front end

Slide 85

Slide 85

Front-end development is software development

Slide 86

Slide 86

Disclaimer #3 Nothing explained here today is new

Slide 87

Slide 87

Slide 88

Slide 88

Slide 89

Slide 89

Wrapping up

Slide 90

Slide 90

Wrapping up Organize code around business rules, not frameworks

Slide 91

Slide 91

Wrapping up Organize code around business rules, not frameworks Dependency Rule Keep details away from the core

Slide 92

Slide 92

Wrapping up Organize code around business rules, not frameworks Make everything easy to test Dependency Rule Keep details away from the core

Slide 93

Slide 93

Wrapping up Organize code around business rules, not frameworks Dependency Rule Keep details away from the core Make everything easy to test Front-end development is software development

Slide 94

Slide 94

This is not about writing good software, but how to build software that can change over time

Slide 95

Slide 95

Ok Adri this is cool Where should I start?

Slide 96

Slide 96

Ok Adri this is cool Where should I start? noti.st/afontcu

Slide 97

Slide 97

👋 That’s all! @afontq afontcu.dev noti.st/afontcu