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