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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Claudio Ortolina
December 02, 2015
Programming
1
170
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
63
Back on your Feet
cloud8421
0
87
Idiomatic Elixir
cloud8421
1
550
GenStage in the Kitchen
cloud8421
4
760
Being a happy developer
cloud8421
2
100
Cooking Lessons with Vim and Tmux
cloud8421
3
280
Rails.vim Projections
cloud8421
0
420
Other Decks in Programming
See All in Programming
米国のサイバーセキュリティタイムラインと見る Goの暗号パッケージの進化
tomtwinkle
2
540
AIに任せる範囲を安全に広げるためにやっていること
fukucheee
0
130
PJのドキュメントを全部Git管理にしたら、一番喜んだのはAIだった
nanaism
0
250
メタプログラミングで実現する「コードを仕様にする」仕組み/nikkei-tech-talk43
nikkei_engineer_recruiting
0
170
モジュラモノリスにおける境界をGoのinternalパッケージで守る
magavel
0
3.5k
Vuetify 3 → 4 何が変わった?差分と移行ポイント10分まとめ
koukimiura
0
110
ロボットのための工場に灯りは要らない
watany
10
2.3k
SourceGeneratorのマーカー属性問題について
htkym
0
180
Go 1.26でのsliceのメモリアロケーション最適化 / Go 1.26 リリースパーティ #go126party
mazrean
1
370
The Ralph Wiggum Loop: First Principles of Autonomous Development
sembayui
0
3.7k
エージェント開発初心者の僕がエージェントを作った話と今後やりたいこと
thasu0123
0
240
ポーリング処理廃止によるイベント駆動アーキテクチャへの移行
seitarof
1
450
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
50
14k
GraphQLの誤解/rethinking-graphql
sonatard
75
11k
Six Lessons from altMBA
skipperchong
29
4.2k
Building a Scalable Design System with Sketch
lauravandoore
463
34k
New Earth Scene 8
popppiees
1
1.7k
Heart Work Chapter 1 - Part 1
lfama
PRO
5
35k
Everyday Curiosity
cassininazir
0
160
Embracing the Ebb and Flow
colly
88
5k
Thoughts on Productivity
jonyablonski
75
5.1k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
54k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
180
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.4k
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!