The Art(?) of Front-end Architecture

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

What are* we aiming for?

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

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

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

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

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

Business stuff Delivery stuff

Business stuff Delivery stuff

Business stuff Delivery stuff

Business stuff Delivery stuff

Delivery stuff

Domain Use cases Delivery stuff

Domain

Domain Use cases

Domain Use cases Delivery

This layer has two streams of data

Infrastructure

Infrastructure UI / Input

Infrastructure API repositories Cookies Web Storage

UI ¯_(ツ)_/¯

What does layer really mean?

Dependencies only point inwards

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

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

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

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

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

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

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

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

The Web is a delivery mechanism

It is not the center. It is external.

It is really, really hard to get it right

We need to adapt its content for our inner layers

We need to adapt its content for our inner layers

Domain Use cases Infrastructure Adapter UI

Domain Use cases Infrastructure Adapter UI

Domain Use cases Infrastructure Adapter UI Independent of the framework

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

Domain Use cases Infrastructure Independent of the framework

Domain Use cases Infrastructure Independent of the framework

infra UI adapter app* domain

infra đŸ‘© UI adapter app* domain

Actions Reducers View State Actions Mutations View State

infra đŸ‘© UI adapter app domain

infra state đŸ‘© UI mutations actions app domain

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 {} }

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 {} }

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 {} }

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) } }

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

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

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

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) }) } }

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) }) } }

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’) } ) })

infra UI adapter app domain

Disclaimer #1 You might not need any of this

Disclaimer #1 You might want some parts of it

Disclaimer #2 This talk was actually about the front end

Front-end development is software development

Disclaimer #3 Nothing explained here today is new

Wrapping up

Wrapping up Organize code around business rules, not frameworks

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

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

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

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

Ok Adri this is cool Where should I start?

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

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