Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Duct for beginners.

Duct for beginners.

How to use Integrant and Duct.

Nakamura, Ryotaro

February 16, 2018
Tweet

More Decks by Nakamura, Ryotaro

Other Decks in Technology

Transcript

  1. Ringの初期化例 4 (def endpoint “http://localhost/health”) (defn handler [request] (get-health endpoint))

    (defn -main [] (jetty/run-jetty handler {:port 3000})) 他のサービスの状態を調べるハンドラ endpointに対するヘルスチェックの結果を返す
  2. よくないところ 5 (def endpoint “http://localhost/health”) (defn handler [request] (get-health endpoint))

    (defn -main [] (jetty/run-jetty handler {:port 3000})) グローバル変数による依存解決 変数のスコープが必要以上に大きい mainからendpointが見える
  3. パラメタでスコープを小さくする 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”)
  4. 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))
  5. コンフィグの展開 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 {} ... }
  6. プロジェクトの構造 22 lein newでテンプレートからプロジェクトを作れる プロジェクトの構造 lein new duct ebis +api

    +ataraxy {{project}} ├── src │ └── {{project}} │ ├── boundary │ │ └── {{boundary}}.clj │ ├── handler │ │ └── {{handler}}.clj │ └── main.clj
  7. 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://..” #(..)))
  8. 事例 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)
  9. モジュール化の方針 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
  10. モジュール側 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
  11. アプリケーション側 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)) ..)
  12. アプリケーション側 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)))
  13. 参考資料 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