The Art of Front-end Architecture

A presentation at FrontFest 2020 in February 2020 in Madrid, Spain by Adrià Fontcuberta

Slide 1

Slide 1

Disclaimer This talk is not about the front end

Slide 2

Slide 2

Disclaimer This talk is about software architecture

Slide 3

Slide 3

Slide 4

Slide 4

We asked for responsibilities

Slide 5

Slide 5

The Art(?) of Front-end Architecture

Slide 6

Slide 6

👋 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 7

Slide 7

What are* we aiming for?

Slide 8

Slide 8

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

Slide 9

Slide 9

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

Slide 10

Slide 10

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

Slide 11

Slide 11

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 12

Slide 12

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

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

Slide 21

Slide 21

Slide 22

Slide 22

Slide 23

Slide 23

Slide 24

Slide 24

Slide 25

Slide 25

Slide 26

Slide 26

Slide 27

Slide 27

Slide 28

Slide 28

Business stuff Delivery stuff

Slide 29

Slide 29

Slide 30

Slide 30

Business stuff Delivery stuff

Slide 31

Slide 31

Business stuff Delivery stuff

Slide 32

Slide 32

Business stuff Delivery stuff

Slide 33

Slide 33

Delivery stuff

Slide 34

Slide 34

Domain Use cases Delivery stuff

Slide 35

Slide 35

Slide 36

Slide 36

Domain

Slide 37

Slide 37

Domain Use cases

Slide 38

Slide 38

Domain Use cases Delivery

Slide 39

Slide 39

Slide 40

Slide 40

This layer has two streams of data

Slide 41

Slide 41

Slide 42

Slide 42

Slide 43

Slide 43

Infrastructure

Slide 44

Slide 44

Infrastructure UI / Input

Slide 45

Slide 45

Infrastructure API repositories Cookies Web Storage

Slide 46

Slide 46

UI ¯_(ツ)_/¯

Slide 47

Slide 47

What does layer really mean?

Slide 48

Slide 48

Slide 49

Slide 49

Dependencies only point inwards

Slide 50

Slide 50

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

Slide 51

Slide 51

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

Slide 52

Slide 52

Slide 53

Slide 53

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

Slide 54

Slide 54

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

Slide 55

Slide 55

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

Slide 56

Slide 56

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

Slide 57

Slide 57

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

Slide 58

Slide 58

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

Slide 59

Slide 59

Slide 60

Slide 60

Slide 61

Slide 61

The Web is a delivery mechanism

Slide 62

Slide 62

It is not the center. It is external.

Slide 63

Slide 63

It is really, really hard to get it right

Slide 64

Slide 64

We need to adapt its content for our inner layers

Slide 65

Slide 65

We need to adapt its content for our inner layers

Slide 66

Slide 66

Domain Use cases Infrastructure Adapter UI

Slide 67

Slide 67

Domain Use cases Infrastructure Adapter UI

Slide 68

Slide 68

Domain Use cases Infrastructure Adapter UI Independent of the framework

Slide 69

Slide 69

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

Slide 70

Slide 70

Domain Use cases Infrastructure Independent of the framework

Slide 71

Slide 71

Domain Use cases Infrastructure Independent of the framework

Slide 72

Slide 72

Slide 73

Slide 73

Slide 74

Slide 74

Slide 75

Slide 75

infra UI adapter app* domain

Slide 76

Slide 76

infra 👩 UI adapter app* domain

Slide 77

Slide 77

Actions Reducers View State Actions Mutations View State

Slide 78

Slide 78

infra 👩 UI adapter app domain

Slide 79

Slide 79

infra state 👩 UI mutations actions app domain

Slide 80

Slide 80

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 81

Slide 81

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 82

Slide 82

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 83

Slide 83

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 84

Slide 84

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

Slide 85

Slide 85

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

Slide 86

Slide 86

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

Slide 87

Slide 87

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 88

Slide 88

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 89

Slide 89

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 90

Slide 90

src/ domain/ application/ infrastructure/ ui/

Slide 91

Slide 91

infra UI adapter app domain

Slide 92

Slide 92

Disclaimer #1 You might not need any of this

Slide 93

Slide 93

Disclaimer #1 You might want some parts of it

Slide 94

Slide 94

Disclaimer #2 This talk was actually about the front end

Slide 95

Slide 95

Front-end development is software development

Slide 96

Slide 96

Disclaimer #3 Nothing explained here today is new

Slide 97

Slide 97

Slide 98

Slide 98

Slide 99

Slide 99

Wrapping up

Slide 100

Slide 100

Wrapping up Organize code around business rules, not frameworks

Slide 101

Slide 101

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

Slide 102

Slide 102

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

Slide 103

Slide 103

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 104

Slide 104

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

Slide 105

Slide 105

Ok Adri this is cool Where should I start?

Slide 106

Slide 106

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

Slide 107

Slide 107

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