extending redux in the server side

Post on 16-Mar-2018

161 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Nacho Martín @nacmartin

AgentConf January 2018

Extending Redux in the Server Side

Nacho Martín (Spanish parents do actually pick this name for their kids)

I write code at Limenius.

We build tailor-made projects, and provide consultancy and formation.

We organize React Alicante.

And we are very happy with React and React Native.

http://slides.com/jenyaterpil/redux-from-twitter-hype-to-production#/

http://slides.com/jenyaterpil/redux-from-twitter-hype-to-production#/

Redux: Three principles

The state of the whole application is stored in an object tree within a single store.

The only way to change the state is to emit an action, an object describing what happened.

To specify how the state tree is transformed by actions, you write pure reducers.

Redux as async reduce

state = actions.reduce(reducer, initialState)

newState = reducer(action, state)

A particular way of thinking.

Useful to model certain problems.

What are we going to talk about

Photo by Tim Trad

MAP

TERRITORY&

PLAN

TERRITORY&

Typical case

Create

Read

Update

Delete

If my DB has…

Typical case

Create

Read

Update

Delete

If my DB has… …and my REST API has…

POST

GET

PUT

DELETE+

Typical case

Create

Read

Update

Delete

If my DB has… …and my REST API has…

POST

GET

PUT

DELETE+ =

…my project will be…

by Adam Morse

TABLES, TABLES EVERYWHERE 🎉

“Chat tutorial paradigm”

SENDHi there! SEND

SERVERemit(‘chat_message’, ‘hi there!’)

(Whenever there is a websocket)

“Chat tutorial paradigm”

SEND SEND

SERVERemit(‘chat_message’, ‘hi there!’)

socket.on(‘chat_message', function(msg){ io.emit(‘chat_message', msg); });

(Whenever there is a websocket)

“Chat tutorial paradigm”

SEND SEND

Hi there!

SERVERemit(‘chat_message’, ‘hi there!’)

socket.on(‘chat_message', function(msg){ io.emit(‘chat_message', msg); });

socket.on(‘chat_message', function(msg){ addMessage(msg)); });

(Whenever there is a websocket)

Typically we also want

SEND SEND

Hi there!

SERVERemit(‘chat_message’, ‘hi there!’)

socket.on(‘chat_message', function(msg){ io.emit(‘chat_message', msg); });

socket.on(‘chat_message', function(msg){ addMessage(msg)); });

store(‘hi there!’)

by Mathyas Kurmann

MESSAGES, MESSAGES EVERYWHERE 🎉

Multiple users manipulate the same resource.

And they need to be notified in real time of the changes.

Our particular case study

Editor for nutritionists that several users can manipulate in collaboration.

Games (cards, domino, quizzes…).

Example: Quiz Game (Multiplayer)

User picks answer 3

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

Example: Quiz Game (Multiplayer)

User picks answer 3

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

Event

Example: Quiz Game (Multiplayer)

User picks answer 3Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

Event

Example: Quiz Game (Multiplayer)

User picks answer 3

Event’

Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

{type: answer player: 1, answer:3, correct: false}

Event

Example: Quiz Game (Multiplayer)

User picks answer 3

Event’

Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

{type: answer player: 1, answer:3, correct: false}

Event

Mark answer as selected

Example: Quiz Game (Multiplayer)

User picks answer 3Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

Event

Example: Quiz Game (Multiplayer)

User picks answer 3

Event’

Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

{type: new_question question: q}

Event

Example: Quiz Game (Multiplayer)

User picks answer 3

Event’

Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

{type: new_question question: q}

Event

Change question

Example: Quiz Game (Multiplayer)

User picks answer 3Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

Event

Example: Quiz Game (Multiplayer)

User picks answer 3

Event’

Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

{type: new_scores scores: sc}

Event

Example: Quiz Game (Multiplayer)

User picks answer 3

Event’

Is the answer correct? Has everybody else answered? Should we ask a new question? Is the game over?

Mark answer as selected

SERVER CLIENTCLIENT

State State’ State

{type: answer answer:3}

{type: new_scores scores: sc}

Event

Update scores

Example: Quiz Game (Multiplayer)

Events’

SERVER CLIENT

State State’Events

Two vocabularies of eventsTwo representations of the stateTwo ways of calculating the state

Example: Quiz Game (Multiplayer)

Events’

SERVER CLIENT

State State’Events

Two applications :_(

Best situation

User disconnected! 💀 SERVERCLIENT

State State’🎉

Best situation

User reconnected! SERVERCLIENT

State State’Plz give me the state

Best situation

SERVERCLIENT

State State’{get_state: state}

🎉

SERVER

State

CLIENT

State

CLIENT

State

SERVER

State

CLIENT

State

CLIENT

State

Event

SERVER

State

CLIENT

State

CLIENT

State

Event

Update state

SERVER

State

CLIENT

State

CLIENT

State

Event

State

Update state

State

Making the backend play well with Redux

A part of the client’s Redux state lives in the server.

The client changes it sending events to the server.

The server pushes (slices of) the state to the clients.

Let’s build the simplest example

With Redux directly in node.js

Views

Actions

Reducers

StateStore

Views

Actions

Reducers

StateStore

Events

Store

State

Reducers

Client Server

Views

Actions

Reducers

StateStore

Events

Store

State

Reducers

Client Server

<Grid> {board.map((row, idxRow) => ( <Row idx={idxRow} key={idxRow}> {row.map((tile, idx) => ( <Tile onClick={() => this.socket.emit("game:play", { x: idx, y: idxRow })} key={idx} idx={idx} tile={tile} /> ))} </Row> ))} </Grid>

Client

Views

Actions

Reducers

StateStore

Events

Store

State

Reducers

Client Server

io.on("connection", function(socket) { //… socket.on("game:play", function(data) { store.dispatch({ type: GAME_PLAY, x: data.x, y: data.y }); }); socket.on("game:reset", function(data) { store.dispatch({ type: GAME_RESET }); });});

Server

const reducer = function(state = initialState, action = {}) { switch (action.type) { case GAME_PLAY: const { stateAfterPlayer, waitingForOpponent } = makePlayerMove(state, { x: action.x, y: action.y }); if (!waitingForOpponent) { return stateAfterPlayer; } return makeAIMove(stateAfterPlayer); case GAME_RESET: return initialState; default: return state; }};

Server

Views

Actions

Reducers

StateStore

Events

Store

State

Reducers

Client Server

observeStore( store, state => state, state => { socket.emit("game_update", state) } );

function observeStore(store, select, onChange) { let currentState;

function handleChange() { let nextState = select(store.getState()); if (nextState !== currentState) { currentState = nextState; onChange(currentState); } }

let unsubscribe = store.subscribe(handleChange); handleChange(); return unsubscribe;}

Server

componentDidMount() { const { dispatch } = this.props; let socket = ioClient("http://localhost:3080"); socket.on("game_update", gameState => dispatch(updateGame(gameState))); }

Client

Views

Actions

Reducers

StateStore

Events

Store

State

Reducers

Client Server

export default function reducer(state = initialState, action = {}) { switch (action.type) { case GAME_UPDATE: return { ...state, board: action.game.board, phase: action.game.phase };

default: return state; }}

export function updateGame(game) { return { type: GAME_UPDATE, game };}

Client

Views

Actions

Reducers

StateStore

Events

Store

State

Reducers

Client Server

const mapStateToProps = state => ({ board: state.board, phase: state.phase });

export default connect(mapStateToProps)(Board);

Client

With Elixir OTP

Erlang: The Movie

Hello Mike.

Used in

Databases, gaming, telecom, message passing…

“Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability”

😍OPEN TELECOM PLATFORM😍

Supervisor

GenServerGenServerSupervisor

GenServerGenServer GenServer

OTP Supervision trees

Gen Server callbacks

defmodule Tictactoe.GameServer do use GenServer

#Callbacks def init(args) do {:ok, state} end

def handle_cast(request, state) do {:noreply, new_state} endend

state = actions.reduce(reducer, initialState)

newState = reducer(action, state);

Views

Actions

Reducers

StateStore

Events

GenServer

State

Callbacks

Client Server

defmodule TictactoeWeb.GameChannel do use Phoenix.Channel

def join("game", _message, socket) do game = Tictactoe.GameServer.getGameState() {:ok, game, socket} end

def handle_in("game:play", %{"x" => x, "y" => y}, socket) do Tictactoe.GameServer.play(x, y) {:noreply, socket} end

def handle_in("game:reset", _, socket) do Tictactoe.GameServer.reset() {:noreply, socket} endend

def init(:ok) do {:ok, %State{}} end

const reducer = function(state = initialState, action = {}) { switch (action.type) { //… default: return state; }};

const reducer = function(state, action) { switch (action.type) { //.. case GAME_RESET: return initialState; //… }};

def handle_cast(%{:action => :reset}, _state) do state = %State{} Endpoint.broadcast(“game", “game_update", serialize_game(state)) {:noreply, state} end

def handle_cast(%{:action => :play, :x => x, :y => y}, state) do case can_move(state, x, y) do :true -> state1 = put_in state.board[y][x], "X" state2 = state |> check_finished |> make_random_move |> check_finished Endpoint.broadcast( “game", “game_update", serialize_game(state2)) state2 :false -> state end {:noreply, state} end

const reducer = function(state, action) { switch (action.type) { case GAME_PLAY: const { stateAfterPlayer, waitingForOpponent } = makePlayerMove(state, { x: action.x, y: action.y }); if (!waitingForOpponent) { return stateAfterPlayer; } return makeAIMove(stateAf); //.. }};

Demo

https://github.com/Limenius/tictactoe

CAN WE THINK ABOUT THIS

AS AN IN-MEMORY DATABASE? 🤔

by Sindre Aalberg

And can we think about this as Event Sourcing?

CLIENT

State

SERVER

And can we think about this as Event Sourcing?

CLIENT

State

Events

SERVER

And can we think about this as Event Sourcing?

CLIENT

State

Events

SERVERLog

And can we think about this as Event Sourcing?

CLIENT

State

Events

SERVER

View

Log

Queries

And can we think about this as Event Sourcing?

CLIENT

State

Events

SERVER

View

Log

Queries

And can we think about this as Event Sourcing?

CLIENT

State

Events

SERVER

View

Log

Another view

Queries

And can we think about this as Event Sourcing?

CLIENT

State

Events

SERVERLog

Another view

View (State)

And can we think about this as Event Sourcing?

CLIENT

State

Events

Subscribe

SERVERLog

Another view

View (State)

FrontendBackendDatabase

THE GAME OF CRUD

FrontendBackendDatabase

THE GAME OF CRUD

FrontendBackendDatabase

THE GAME OF CRUD

FrontendBackendDatabase

THE GAME OF CRUD

FrontendBackendDatabase

LET’S PLAY A DIFFERENT GAME

FrontendBackendDatabase

LET’S PLAY A DIFFERENT GAME

THANKS!

@nacmartin nacho@limenius.com

top related