Duct で Web サーバーを構築しよう
Clojure 製の Web サーバー構築ライブラリとして Duct というものがあります。 この分野については他にも pedestal とか Biff とかいくつかありますが、今回は Duct について話します。
Duct の特徴
- 関数同士の依存関係を config.edn という設定ファイルに記述することで疎結合にする
- defmethod をいい感じに使うことで上記を実現している
言ってしまえばこれだけですが、初心者には意味不明でしょうし、ある程度 Web サーバーの設計に詳しい人でないとこのメリットもわかりません。 どんな言語でも課題になるのは関数同士の依存関係、つまり関数の中から他の関数を呼び出すという普通の行為が結構密結合に感じることもあって、そういうのをなんとかできるのが Duct というわけです。 なので、 Rails みたいなフルスタックな Web フレームワークにあるようなデータベースへのアクセスであるとか HTTP リクエストのルーティングの解決であるとかそういう機能は Duct にはありませんし、それが主眼でもありません。
「何を言っているんだお前は」という感じだと思いますので、とりあえず使ってみましょう。
Duct のプロジェクトを用意する
本来なら lein new duct
で Duct のプロジェクトを作り始める事ができるのですが、0 からスタートすると手間が多いのである程度作っておきました。
次のリポジトリを見てください。
次のような機能を実装済みです
- PostgreSQL によるデータベースアクセス関係
- Docker で DB 起動可能
- hikaricp で connection pool 対応済み
- database migration
- コマンドラインで扱いやすいので ragtime ではなく golang-migrate/migrate を採用
- metosin/reitit による routing
- Handler のサンプル
- Test 用の config.edn とサンプル
このリポジトリを clone したり Fork したりして使ってみてください。
duct-init リポジトリの使い方
Setup
まず homebrew と docker、そして docker compose を使えるようにしておいてください。 homebrew は Linux 環境でも動かすことができるのでインストールをおすすめします (Windows は WSL などの Linux 仮想環境を使ってください)。 リポジトリの中で
./runner setup
をすることで duct-init プロジェクトに必要なツールをインストールします。
duct-init プロジェクトではタスクランナーとして runner というシェルスクリプトを用意しています。
runner
の内容を理解する必要はありませんが、興味のある方はコードを読んでみてください。
Database の起動と migration
次のコマンドを実行すると docker compose を使って PostgreSQL が起動します。
./runner up
この状態で別のターミナルで
./runner migrate
を実行してください。 Database の migration と seeds データの読み込みを行います。
migration は db/migrations ディレクトリにあるファイルから実行します。
db
├── migrations
│ ├── 20221023001_init.down.sql
│ └── 20221023001_init.up.sql
└── seeds
└── dummy_data.sql
db/migrations
以下に [timestamp]_[name].{up,down}.sql
のフォーマットでファイルを置いてください。
timestamp の古い SQL から順番に実行してデータベースのテーブルやカラムなどを作成します。
migration を進めるときは ./runner migrate
、戻すときは ./runner migrate-down
を使います。
seeds データは db/seeds
ディレクトリ以下にある SQL ファイルをすべて開発用 DB に対して実行します。
./runner migrate
により seeds データは自動で読み込まれます。
サーバーの起動
準備が整ったのでアプリケーションサーバーを起動してみましょう。 次のコマンドで Leiningen の REPL を起動することができます。
./runner repl
Leiningen は Clojure のパッケージ管理やタスク管理などをできるツールです。
パッケージのインストールが終わると dev
という名前空間で REPL が起動します。
ここで、 (go)
という関数を実行するとサーバーが 3000 ポートで起動します。
dev=> (go)
:duct.server.http.jetty/starting-server {:port 3000}
:initiated
ファイルの変更をサーバーに反映したい場合は
dev=> (reset)
:reloading (...)
:resumed
または
dev=> (auto-reset)
を使います。
テストの実行
テストを実行するには次のコマンドを使います。
./runner test
引数で名前空間を指定することで特定のテストだけ実行することもできます。
./runner test foo.bar-test
まとめ
今回は neumann-tokyo/duct-init の基本的な使い方を説明しました。 duct の経験が多少ある方はリポジトリのコードを見て活用してみてください。
引き続き、数回にわけて duct の入門記事を公開する予定です。