Toyokumo Tech Blog

トヨクモ株式会社の開発本部のブログです。

Clojureで作るAPI 設定をednで管理する

[連載]Clojureで作るAPIの7記事目です。

前回の記事はこちらです。

tech.toyokumo.co.jp

この記事ではアプリケーションの設定をClojureのソースではない設定ファイルで管理できるようにしていきます。

なぜ設定ファイルで管理する必要があるのか

実際のアプリケーションでは、開発環境・ステージング環境・本番環境と複数の環境で動くようにしなければいけません。 DBの接続先や外部のAPIキーなど、動作環境によって切り替えたい値は複数あります。

設定値は固定値だけでなく環境変数から読み込んだものであったりするでしょうが、設定値を各所に書き散らかしてしまうと保守性が下がるのでそれらを一箇所にまとめて管理したいです。

今回はそういったことを実現していきます。

aeroを追加

この目的のためにaeroというライブラリがあります。こちらを使っていきましょう。

github.com

まずライブラリを deps.edn に追加します。

;; ./deps.edn
{:paths ["src" "resources"] ;; resourcesを追加
 :deps {org.clojure/clojure {:mvn/version "1.11.1"}
        info.sunng/ring-jetty9-adapter {:mvn/version "0.17.6" :exclusions [org.slf4j/slf4j-api]}
        spootnik/unilog {:mvn/version "0.7.30"}
        com.stuartsierra/component {:mvn/version "1.1.0"}
        ;; ライブラリを追加
        aero/aero {:mvn/version "1.1.6"}}
 :aliases {:dev {:extra-paths ["dev"]}
           :build {:deps {io.github.clojure/tools.build {:git/tag "v0.8.2" :git/sha "ba1a2bf"}}
                   :ns-default build}}}

前回の記事でJARファイルにビルドできるようにしました。 多くのケースでJARをビルドした環境とそれを実行する環境とは異なるはずです。 ビルドした環境にはあるが実行環境にないファイルをビルドしたJARアプリケーションから参照したい場合は、そのファイルがJARに含まれるようにする必要があります。

:paths にresourcesを追加すると、resources下のファイルがJARに含まれるようになります。 その上で設定ファイルを ./resources/config.edn というパスに配置することで実行環境でも設定ファイルを参照できるようにします。

設定ファイルの追加

./recources/config.edn に設定をaeroの作法に従って書いていきます。

;; ./resources/config.edn
{:server {:opts {:host "localhost"
                 :port #long #profile {:default 5000
                                       :dev 8000}
                 :join? false}}}

ひとまず前回までの記事で立ち上げたWebサーバーの設定だけ追加しています。

ほとんど単なるClojureのMapですが、2つだけタグリテラルを使っています。

#longは基本的に戻り値はStringであるところをlong型でパースして取得することを可能にします。他には #double#keyword などがあります。

#profileはprofileがMapのキーに一致する値を返します。profileはconfig.ednをパースする関数を呼び出すときに引数として与えます。この例だと :profile:dev なら8000、それ以外なら5000ということです。

これ以外にもタグリテラルはあります。ここでは解説しませんが、今後新しいものが登場するたびに説明するのでご安心ください。

設定の読み込み

cljapi.config/read-config に設定を読み込む関数を定義します。

;; ./src/cljapi/config.clj
(ns cljapi.config
  (:require
   [aero.core :as aero]
   [clojure.java.io :as io]))

(defn read-config [profile]
  {:pre [(contains? #{:dev :prod :test} profile)]}
  (-> (io/resource "config.edn")
      (aero/read-config {:profile profile})
      (assoc :profile profile)))

:pre は初登場ですが、これは関数の事前条件をチェックするために使います。上記の例だと引数profileが、:dev, :prod, :testのいずれかでなければ AssertionError 例外が投げられます。

io/resource はクラスパスに含まれるパスの一致するファイルから java.net.URLを取得する関数です。先ほどconfig.ednはresources下に配置し、resourcesをdeps.ednで :paths に指定してあるのでURLが取得できるということです。

REPLで確かめてみましょう。

read-config

Systemとの統合

最後にSystemと統合し、開発環境では :dev profileで、本番環境では :prod profileで起動できるようにします。

まず cljapi.system を次のようにします。

;; ./src/cljapi/system.clj
(ns cljapi.system
  (:require
   [cljapi.component.handler :as c.handler]
   [cljapi.component.server :as c.server]
   [cljapi.config :as config]
   [com.stuartsierra.component :as component]))

(defn- new-system [config]
  (component/system-map
   :handler (c.handler/map->Handler {})
   :server (component/using
            (c.server/map->Jetty9Server (:server config))
            [:handler])))

(defn start [profile]
  (let [config (config/read-config profile)
        system (new-system config)]
    (component/start system)))

(defn stop [system]
  (component/stop system))

これまで c.server/map->Jetty9Server に直接Mapを与えていたところをconfig.ednから読み込んだ設定を使うようにしています。

開発用の user も修正します。

;; ./dev/user.clj
(ns user
  (:require
   [cljapi.system :as system]))

(defonce system (atom nil))

(defn start []
  (reset! system (system/start :dev))) ;; startに :dev を与える修正

(defn stop []
  (when @system
    (reset! system (system/stop @system))))

(defn go []
  (stop)
  (start))

ここまで修正ができたら次の2つを確かめてみましょう。

  • REPLを立ち上げて (go) を評価して curl http://localhost:8000 を実行すると Hello, Clojure API と返ってくる
  • make build して java -jar target/cljapi.jar を実行して curl http://localhost:5000 を実行すると Hello, Clojure API と返ってくる

profileによって異なる設定が反映されていることが確認できました。

おわりに

ここまでで、設定ファイルをソースコードの外に配置し、コンテキストによって異なる設定でアプリケーションを起動できるようになりました。

コードはGitHubのリポジトリに上げてあります。 07_設定をednで管理するというタグからご覧ください。

次はロギングの設定を行なっていきます。


トヨクモでは一緒に働いてくれる技術が好きなエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。