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
Duct for beginners.
Search
Nakamura, Ryotaro
February 16, 2018
Technology
0
3.8k
Duct for beginners.
How to use Integrant and Duct.
Nakamura, Ryotaro
February 16, 2018
Tweet
Share
More Decks by Nakamura, Ryotaro
See All by Nakamura, Ryotaro
Learn Go in 15 minutes
nryotaro
0
27
An introduction of statistical learning
nryotaro
0
27
Seven architectural patterns
nryotaro
1
80
Improving Performance with Parallel Programming
nryotaro
0
42
Other Decks in Technology
See All in Technology
どうするコスト最適化のトレードオフ
tetsuyaooooo
1
690
EMとして2023年度に頑張ったこと / What we did well in FY2023 as a EM
pauli
1
200
DMM.com アルファ室採用案内資料
hsugita
1
210
Autonomous Database Cloud 技術詳細 / adb-s_technical_detail_jp
oracle4engineer
PRO
14
35k
アクセス制御にまつわる改善 / Improving access control
itkq
0
580
IaCジェネレーターとBedrockで詳細設計書を生成してみた
tsukasa_ishimaru
4
860
Building a RAG-poweredAI chat appwith Python and VS Code
pamelafox
0
140
[新卒向け研修資料] テスト文字列に「うんこ」と入れるな(2024年版)
infiniteloop_inc
4
17k
IPUT App Dev. Co. -Overview 2024/4
iputapp
0
120
今日からできる!簡単 .NET 高速化 Tips -2024 edition-
xin9le
7
3.4k
Além do else! Categorizando Pokemóns com Pattern Matching no JavaScript
wmsbill
0
690
BPStudyの200回を中心にIT業界を振り返る。そしてこれから
haru860
3
390
Featured
See All Featured
Making Projects Easy
brettharned
109
5.5k
5 minutes of I Can Smell Your CMS
philhawksworth
199
19k
Building Applications with DynamoDB
mza
88
5.6k
Agile that works and the tools we love
rasmusluckow
325
20k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
34
8.9k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
155
14k
Practical Orchestrator
shlominoach
183
9.7k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
19
6.9k
Scaling GitHub
holman
457
140k
Building a Scalable Design System with Sketch
lauravandoore
457
32k
The Cult of Friendly URLs
andyhume
74
5.7k
YesSQL, Process and Tooling at Scale
rocio
165
13k
Transcript
はじめてのDuct 中村 遼太郎
話すこと 2 IntegrantでこまってDuctを使うようになった話 • Integrant ◦ 概要 ◦ 困ったこと •
Duct ◦ 概要 ◦ Boundaries ◦ Moduleの作り方
Integrant 3
Ringの初期化例 4 (def endpoint “http://localhost/health”) (defn handler [request] (get-health endpoint))
(defn -main [] (jetty/run-jetty handler {:port 3000})) 他のサービスの状態を調べるハンドラ endpointに対するヘルスチェックの結果を返す
よくないところ 5 (def endpoint “http://localhost/health”) (defn handler [request] (get-health endpoint))
(defn -main [] (jetty/run-jetty handler {:port 3000})) グローバル変数による依存解決 変数のスコープが必要以上に大きい mainからendpointが見える
パラメタでスコープを小さくする 6 パラメタによる依存解決 設定とリクエストまじり,パラメタが複雑になる (defn wrap-handler [handler ep] (fn [request]
(handler (assoc request :endpoint ep)))) (defn handler [request] (get-health (:endpoint request))) (wrap-handler handler “http://localhost/health”)
Integrant 7 クロージャによる依存解決 クロージャでローカル変数を個別に渡す (defmethod ig/init-key :ebis/handler [_ endpoint] (fn
[request] (get-health endpoint))) (defmethod ig/init-key :ebis/jetty [_ {:keys [handler] :as options}] (jetty/run-jetty handler options))
関心ごと分離 8 コンフィグマップ(構造) Integrantはシステムの実装と構造を分ける {:ebis/handler “http://localhost/health” :ebis/jetty {:handler #ig/ref :ebis/handler
:port 3000}}
Integrantがやること 9 キーワードをオブジェクトに置き換える Integrantはシステムの実装と構造を分ける {:ebis/handler “http://localhost/health” :ebis/jetty {:handler #ig/ref :ebis/handler
:port 3000}} init-key
Integrantからみたシステム 10 コードの木 Integrantはコードの幹にある integrantは コードを呼び出す コードが ライブラリを呼ぶ https://skillsmatter.com/skillscasts/8717-arachne-building-a-framework-in-clojure
Integrantからみたシステム 11 システムの実装と構造の分離 Integrantは幹の部分から実装とコードを分けていく James Reeves, Enter Integrant: a micro-framework
for data-driven-architecture 実装
Integrantで困ったこと 12 途中までIntegrantを使う ライブラリの呼び出し部分への適用が最後になる ライブラリと コードが密結合 コンフィグマップ にする順序
ほしいもの 13 途中までIntegrantを使う ➔ ライブラリの呼び出しを楽を疎にするもの ➔ コンフィグマップの適用を楽にするもの 楽に適用できる システムの境界 を疎にできる
ほしいもの 14 途中までIntegrantを使う ➔ ライブラリの呼び出しを楽を疎にするもの ➔ コンフィグマップの適用を楽にするもの 楽に適用できる システムの境界 を疎にできる
Duct Boundaries Duct modules
Duct 15 weavejester If Integrant is the “grammar”, Duct is
attempting to build a “vocabulary”
Integrantの初期化手順 16 コンフィグをもとにシステムを初期化する read init コンフィグを読む コンフィグをオブジェクトに変換する
Ductの初期化手順 17 コンフィグを拡張する中間ステップがある read init コンフィグを読む prep コンフィグを書き換える コンフィグをオブジェクトに変換する
モジュール 18 prepステップでコンフィグを展開する純粋関数 read init prep module.web module.cljs module.sql module.logging
module.ataraxy Ductが提供するもの
コンフィグの展開 19 config.ednで使うモジュールを宣言する コンフィグのロード $ cat resources/ebis/config.edn { :duct.module/logging {}
... } ... $ lein repl user => (dev) :loaded Read Prep init
コンフィグの展開 20 prepでコンフィグを展開する モジュールによるloggerの展開 Read Prep init (dev) => (prep)
:prepped dev => (pprint config) { :duct.logger/timbre {:level :debug, {:appenders {: ...} :duct.logger.timbre/spit {:fname “logs/..”} :duct.module/logging {} ... }
コンフィグの展開 21 展開されたコンフィグでシステムが初期化される サーバの起動 Read Prep init dev => (go)
:duct.server.http.jetty/starting-server {:port 3000} :initiated
プロジェクトの構造 22 lein newでテンプレートからプロジェクトを作れる プロジェクトの構造 lein new duct ebis +api
+ataraxy {{project}} ├── src │ └── {{project}} │ ├── boundary │ │ └── {{boundary}}.clj │ ├── handler │ │ └── {{handler}}.clj │ └── main.clj
Boundaries 23 protocolを介してシステムの外と通信する プロトコルの利用例 (defprotocol Health (get-health [this])) (defrecord HttpHealth
[method url handler] Health (get-health [{:keys [method url handler]}] (handler (method url)))) (defmethod ig/init-key ::http [_ {:keys [url]}] (->HttpHealth http/get “http://..” #(..)))
Ductで困ること 24 必要なモジュールがない https://github.com/duct-framework module.web module.cljs module.sql module.logging module.ataraxy Duct
frameworkが提供するモジュール
事例 25 Google pubsubのモジュールを作る 購読開始処理 (def s-name (SubscriptionName/create “p-id” “s-id”))
(def receiver (reify MessageReceiver (receiveMessage [this message consume] (println (. message getData))))) (def subscriber (Subscriber/newBuilder s-name receiver)) (. (. subscriber build) startAsync)
モジュール化の方針 26 SubscriptionName/createをモジュールに移す 処理の分割 (def s-name → モジュール (SubscriptionName/create “p-id”
“s-id”)) (def receiver → ig/init (reify MessageReceiver (receiveMessage [this message consume] (println (. message getData))))) (def subscriber (Subscriber/newBuilder s-name receiver)) (. (. subscriber build) startAsync)→ protocol
モジュール側 1/3 27 src/duct_hierachy.ednにprepで呼び出すキーを登録 :duct/moduleを継承する {:duct.module/message [:duct/module]}
モジュール側 2/3 28 :duct/moduleを継承したキーがprepで呼び出される :duct/message/pubsubをコンフィグマップに追加 (defmethod ig/init-key :duct.module/message [_ options]
{:fn (fn [config] (core/merge-configs config ; ユーザが定義したコンフィグ {:duct.message/pubsub ;追加するキー {:logger (ig/ref :duct/logger)}}))}) :duct/module
モジュール側 3/3 29 prepで追加されたキーはinitするときに呼び出される SubscriptionNameのインスタンス化 (defmethod ig/init-key :duct.message/pubsub [_ {:keys
[p-id s-id] :as opt}] (assoc opt :s-name (SubscriptionName/create p-id s-id)))
アプリケーション側 1/3 30 config.ednに作ったモジュールを宣言する config.ednにモジュールを宣言 {:duct.message/pubsub {:p-id "clj-p" :s-id "clj-s"}
:duct.module/message {} :ebis.boundary/message #ig/ref :duct.message/pubsub } モジュールが初期化する
アプリケーション側 2/3 31 boundaryでprotocolを宣言する Receiverの宣言 (defprotocol Receiver (start [this]) (stop
[this])) (defrecord PubSubReceiver [subscriber logger] (start [this] (l/log logger :info “start”) (. subscriber startAsync)) ..)
アプリケーション側 3/3 32 Receiverをinit-keyの中で作る Receiverの生成 (defmethod :ig/init-key :ebis.boundary/message [_ {:keys
s-name logger}] (let [subscriber (... (reify MessageReceiver ...)) receiver (->PubSubReceiver subscriber logger)] (start receiver) (receiver)))
おさらい 33 ➔ Integrantだけだとコンフィグマップの適用がたいへん ➔ Ductのモジュールはコンフィグ化を助けてくれる ➔ しかしモジュールの数は少なく,作り方の説明は不十分 ➔ そこでモジュールの作り方を紹介した
参考資料 34 ➔ Arachne: building a framework in Clojure https://skillsmatter.com/skillscasts/8717-arachne-building-a-framework-in-clojure
➔ Productive Duct https://skillsmatter.com/skillscasts/10836-productive-duct ➔ Integrant: a micro-framework for data-driven architecture with James Reeves https://skillsmatter.com/skillscasts/9820-enter-integrant-a-micro-framework-for-data-driven-architecture-with-james-r eeves ➔ Duct, Covered https://skillsmatter.com/skillscasts/7229-duct-covered ➔ Duct Framework and supporting libraries https://github.com/duct-framework