Slide 1

Slide 1 text

PROTOCOLS AND STRUCTS FOR EFFECTIVE DATA FLOW

Slide 2

Slide 2 text

THE DOMAIN CHECK-IN PROCESS NOTIFY

Slide 3

Slide 3 text

RECAP ABOUT PROTOCOLS AND STRUCTS

Slide 4

Slide 4 text

STRUCTS defmodule Checkin do defstruct id: nil, lat: 0.0, lng: 0.0 end

Slide 5

Slide 5 text

STRUCTS %Checkin{id: "123", lat: 51.50, lng: 0.12} struct(Checkin, id: "123", lat: 51.50, lng: 0.12) struct(Checkin, %{id: "123", lat: 51.50, lng: 0.12}) struct(Checkin, id: "123", lat: 51.50, lng: 0.12, ignored: true)

Slide 6

Slide 6 text

PROTOCOLS defprotocol Persistence do def create(thing) end

Slide 7

Slide 7 text

IMPLEMENTATION defimpl Persistence, for: Checkin do def create(checkin) do %Doc{class: "Checkin", fields: Map.from_struct(checkin)} |> Repo.create end end

Slide 8

Slide 8 text

FALLBACK defprotocol Persistence do @fallback_to_any true def create(thing) end

Slide 9

Slide 9 text

FALLBACK defimpl Persistence, for: Any do def create(thing) do class = Map.get(thing, :__struct__) |> to_string %Doc{class: class, fields: Map.from_struct(thing)} |> Repo.create end end

Slide 10

Slide 10 text

USAGE %Checkin{id: "123", lat: 51.50, lng: 0.12} |> Persistence.create

Slide 11

Slide 11 text

FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY

Slide 12

Slide 12 text

FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY

Slide 13

Slide 13 text

IDENTIFY { "meta" : { "version" : 1, "topic" : "checkins", "type" : "create", "request_id" : "15456d4e-782b-11e5-8bcf-feff819cdc9f" }, "data" : { "ref" : "123ASDS", "lat" : 51.50, "lng" : 0.12 } }

Slide 14

Slide 14 text

IDENTIFY defmodule V1.Checkin.Create do defstruct request_id: nil, ref: nil, lat: 0.0, lng: 0.0 end

Slide 15

Slide 15 text

IDENTIFY def identify(payload) do case payload["meta"] do %{"version" => 1, "topic" => "checkins", "type" => "create"} -> V1.Checkins.Create _other -> {:error, :unsupported_payload} end end

Slide 16

Slide 16 text

POPULATE def populate(struct_name, payload) do request_id = get_in(payload, "meta", "request_id") struct_attrs = Map.put(payload["data"], "request_id", request_id) Kernel.struct(struct_name, Utils.atomise_map(struct_attrs)) end

Slide 17

Slide 17 text

ALL TOGETHER NOW message |> identify |> populate(message)

Slide 18

Slide 18 text

WINS STABLE INTERFACE DEFINES A BOUNDARY EXTENSIBLE INTENTION REVEALING

Slide 19

Slide 19 text

FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY

Slide 20

Slide 20 text

LOG defimpl String.Chars, for: V1.Checkins.Create do def to_string(create_struct) do "type=create status=accepted request_id=#{create_struct.request_id} ref={create_struct.ref}" end end

Slide 21

Slide 21 text

LOG def log(item) do :ok = Logger.info item item end

Slide 22

Slide 22 text

ALL TOGETHER NOW message |> identify |> populate(message) |> log

Slide 23

Slide 23 text

FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY

Slide 24

Slide 24 text

PROCESS defimpl Job, for: V1.Checkin.Create do def process(create_struct) do {:ok, new_record} = Persistence.create(create_struct) %V1.Checkin.Created{request_id: create_struct.request_id, record: new_record} end end

Slide 25

Slide 25 text

ALL TOGETHER NOW message |> identify |> populate(message) |> log |> Job.process

Slide 26

Slide 26 text

WINS STABLE INTERFACE DEFINES A BOUNDARY EXTENSIBLE INTENTION REVEALING

Slide 27

Slide 27 text

FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY

Slide 28

Slide 28 text

LOG defimpl String.Chars, for: V1.Checkins.Created do def to_string(created_struct) do "type=create status=processed request_id=#{create_struct.request_id} id={created_struct.record.id}" end end

Slide 29

Slide 29 text

ALL TOGETHER NOW message |> identify |> populate(message) |> log |> Job.process |> log

Slide 30

Slide 30 text

NOTIFY MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY

Slide 31

Slide 31 text

NOTIFY defimpl Notify, for: V1.Checkin.Created do def topics(created_struct), do: ["checkins", "users"] def to_message(created_struct) do %Message{request_id: created_struct.request_id, type: :create, data: %{id: created_struct.record.id}} end end

Slide 32

Slide 32 text

NOTIFY def notify(item) do payload = Notify.to_message(item) topics = Notify.topics(item) PubSub.broadcast(payload, topics) end

Slide 33

Slide 33 text

ALL TOGETHER NOW message |> identify |> populate(message) |> log |> Job.process |> log |> notify

Slide 34

Slide 34 text

EXECUTION MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY LISTENER PROCESS POOL PUBSUB POOL

Slide 35

Slide 35 text

EXTENDING FURTHER ADD NEW STRUCTS IMPLEMENT PROTOCOLS

Slide 36

Slide 36 text

CORE IDEAS ASSIGN INTENT TO DATA DEFINE FUNCTIONALITY AS PROTOCOLS DEFINE STRICT BOUNDARIES BETWEEN STEPS

Slide 37

Slide 37 text

THANK YOU! http://bit.ly/ff-protocols-structs

Slide 38

Slide 38 text

PLUG ELIXIR, JAVASCRIPT, ELM DEVELOPMENT ELIXIR, JAVASCRIPT, ELM TRAINING ASK ME ABOUT IT!