Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Protocols and Structs
Search
Claudio Ortolina
December 02, 2015
Programming
1
160
Protocols and Structs
Processing a data flow in Elixir
Claudio Ortolina
December 02, 2015
Tweet
Share
More Decks by Claudio Ortolina
See All by Claudio Ortolina
The Perils of Large Files
cloud8421
0
62
Back on your Feet
cloud8421
0
80
Idiomatic Elixir
cloud8421
1
540
GenStage in the Kitchen
cloud8421
4
760
Being a happy developer
cloud8421
2
99
Cooking Lessons with Vim and Tmux
cloud8421
3
280
Rails.vim Projections
cloud8421
0
410
Other Decks in Programming
See All in Programming
Comparing decimals in Swift Testing
417_72ki
0
170
兎に角、コードレビュー
mitohato14
0
120
CEDEC 2025 『ゲームにおけるリアルタイム通信への QUIC導入事例の紹介』
segadevtech
3
880
画像コンペでのベースラインモデルの育て方
tattaka
3
1.7k
「リーダーは意思決定する人」って本当?~ 学びを現場で活かす、リーダー4ヶ月目の試行錯誤 ~
marina1017
0
220
エンジニアのための”最低限いい感じ”デザイン入門
shunshobon
0
110
実践 Dev Containers × Claude Code
touyu
1
200
[DevinMeetupTokyo2025] コード書かせないDevinの使い方
takumiyoshikawa
2
280
AIに安心して任せるためにTypeScriptで一意な型を作ろう
arfes0e2b3c
0
370
Claude Code と OpenAI o3 で メタデータ情報を作る
laket
0
130
JetBrainsのAI機能の紹介 #jjug
yusuke
0
200
iOS開発スターターキットの作り方
akidon0000
0
250
Featured
See All Featured
Done Done
chrislema
185
16k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
283
13k
Designing Experiences People Love
moore
142
24k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
44
2.4k
[RailsConf 2023] Rails as a piece of cake
palkan
56
5.8k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
6k
Typedesign – Prime Four
hannesfritz
42
2.8k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3k
Raft: Consensus for Rubyists
vanstee
140
7.1k
Balancing Empowerment & Direction
lara
2
570
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Into the Great Unknown - MozCon
thekraken
40
2k
Transcript
PROTOCOLS AND STRUCTS FOR EFFECTIVE DATA FLOW
THE DOMAIN CHECK-IN PROCESS NOTIFY
RECAP ABOUT PROTOCOLS AND STRUCTS
STRUCTS defmodule Checkin do defstruct id: nil, lat: 0.0, lng:
0.0 end
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)
PROTOCOLS defprotocol Persistence do def create(thing) end
IMPLEMENTATION defimpl Persistence, for: Checkin do def create(checkin) do %Doc{class:
"Checkin", fields: Map.from_struct(checkin)} |> Repo.create end end
FALLBACK defprotocol Persistence do @fallback_to_any true def create(thing) end
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
USAGE %Checkin{id: "123", lat: 51.50, lng: 0.12} |> Persistence.create
FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY
FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY
IDENTIFY { "meta" : { "version" : 1, "topic" :
"checkins", "type" : "create", "request_id" : "15456d4e-782b-11e5-8bcf-feff819cdc9f" }, "data" : { "ref" : "123ASDS", "lat" : 51.50, "lng" : 0.12 } }
IDENTIFY defmodule V1.Checkin.Create do defstruct request_id: nil, ref: nil, lat:
0.0, lng: 0.0 end
IDENTIFY def identify(payload) do case payload["meta"] do %{"version" => 1,
"topic" => "checkins", "type" => "create"} -> V1.Checkins.Create _other -> {:error, :unsupported_payload} end end
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
ALL TOGETHER NOW message |> identify |> populate(message)
WINS STABLE INTERFACE DEFINES A BOUNDARY EXTENSIBLE INTENTION REVEALING
FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY
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
LOG def log(item) do :ok = Logger.info item item end
ALL TOGETHER NOW message |> identify |> populate(message) |> log
FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY
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
ALL TOGETHER NOW message |> identify |> populate(message) |> log
|> Job.process
WINS STABLE INTERFACE DEFINES A BOUNDARY EXTENSIBLE INTENTION REVEALING
FLOW MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY
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
ALL TOGETHER NOW message |> identify |> populate(message) |> log
|> Job.process |> log
NOTIFY MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY
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
NOTIFY def notify(item) do payload = Notify.to_message(item) topics = Notify.topics(item)
PubSub.broadcast(payload, topics) end
ALL TOGETHER NOW message |> identify |> populate(message) |> log
|> Job.process |> log |> notify
EXECUTION MESSAGE IDENTIFY LOG PROCESS LOG NOTIFY LISTENER PROCESS POOL
PUBSUB POOL
EXTENDING FURTHER ADD NEW STRUCTS IMPLEMENT PROTOCOLS
CORE IDEAS ASSIGN INTENT TO DATA DEFINE FUNCTIONALITY AS PROTOCOLS
DEFINE STRICT BOUNDARIES BETWEEN STEPS
THANK YOU! http://bit.ly/ff-protocols-structs
PLUG ELIXIR, JAVASCRIPT, ELM DEVELOPMENT ELIXIR, JAVASCRIPT, ELM TRAINING ASK
ME ABOUT IT!