SQLBoiler から sqlc への移行 —— 選定基準と導入までの実践記録
こんにちは!ソフトウェアエンジニアの inari111 です。
先日 Go Conference 2025 に行ってきました!2 日間参加してきましたが、どのセッションも面白く勉強になりました。次回の Go Conference が今から楽しみです!
それでは、本題に移ります。
背景:SQLBoiler のメンテナンスモード突入
RemitAid のバックエンドには SQLBoiler を使っています。
SQLBoiler は DB のスキーマから Go のコードを生成する ORM です。スキーマファーストのライブラリで、型安全なコードを生成します。
SQLBoiler が v4.17.1 からメンテナンスモードになっており、移行先を探すことになりました。
選定基準
代替のライブラリを選ぶにあたりいくつか条件がありました。
- 学習コストが低いこと
- 開発効率を上げるためにコード生成を利用することができること
- 生成されるコードが型安全であること
移行先の候補
乗り換え先の候補として以下のライブラリを検討しました。
簡単にですが、各ライブラリの紹介をします。
-
sqlc
- SQL ファーストのライブラリ
- ORM ではなく生の SQL を書き、それに対する型安全なコードを生成する
- SQLBoiler の README でも代替案として紹介されている
-
ent
- コードファーストのライブラリ
- Go でスキーマを定義 → コード生成 → DB マイグレーション
-
dbtpl
- 2025 年 5 月 6 日に「xo」から「dbtpl」へ変更
- DB スキーマ/クエリ → テンプレート経由でコード生成
- テンプレートをカスタマイズすることで柔軟なコード生成に対応
sqlc 採用の決め手
各ライブラリの PoC を行い検討した結果、sqlc を採用することにしました。
学習コストが低い
- 生 SQL を書けるため、既存の SQL 知識がそのまま活かせる
- ORM のクエリビルダーを新たに学ぶ必要がない
- ドキュメントが充実しており、すぐに使い始められた
漸進的な移行が可能
- 既存の SQLBoiler コードと共存できる
- 新規機能から段階的に導入できる
- リスクを抑えた移行が可能
今回は十分な検証時間を取れなかったという事情もあり、社内管理画面の API の新規実装から使い始めています。
導入まで
- mysqldump でスキーマ情報のみを dump
- コードを生成するための SQL を用意
-- 例: merchant.sql
-- name: ListAllMerchants :many
SELECT
id,
company_name
FROM
merchants
ORDER BY
id ASC;
- sqlc.yaml を用意
- DB の型と Go の型のマッピング
# sqlc.yaml の例
version: "2"
sql:
- engine: "mysql"
queries: "../queries"
schema: "../schema/schema.sql"
gen:
go:
package: "generated"
out: "../generated"
emit_empty_slices: true # クエリ結果が空の場合、nilではなく空のsliceを返す
sql_package: "database/sql"
overrides:
- db_type: "varchar"
nullable: true
go_type:
import: "database/sql"
type: "Null[string]"
# (省略)
- コード生成 (sqlc generate) を行うコマンドを Makefile に追加
- 生成されたコードを使って処理を実装
# ディレクトリ構造
.
├── generated/ # sqlc が自動生成するコード
│ ├── db.go # データベース接続用のコード
│ ├── models.go # テーブルの構造体定義
│ └── merchant.sql.go # merchant.sql から生成されたコード
└── queries/ # SQL クエリファイル(手動で記述)
└── merchant.sql # merchant テーブル用のクエリ
ざっくりとですが、導入に関して行ったことをご紹介しました。
新規実装から導入するのは容易で、すぐに実装に入ることができました。
query ファイルの設計方針
導入中に1つだけ悩ましい問題がありました。
query ファイルをどの単位で分割するかという問題です。
1 ファイルに全部の SQL を書くと見通しが悪く、保守性も下がります。
私のチームでは Repository の interface を定義する単位で SQL のファイルも分割することにしました。
例:
-
MerchantRepository→merchant.sql -
TransactionRepository→transaction.sql
このルールにより、SQL ファイルとコードの対応が明確になり、保守性が向上しました。
個人的には、SQL 駆動の開発は体験がいいと思っています。
今後の展開
今後は社内管理画面以外の API でも導入を進めていけたらと考えています。
SQLBoiler からの移行は急ぎではないですが、全体の方針は統一していきたいので、新規実装から sqlc を使い、既存の実装も徐々に移行していけたらと思います。
RemitAid では一緒に働く仲間を募集しています。
興味がある方はこちらからどうぞ!
Podcast 「RemiTalk」を最近始めましたので、もし良ければ聴いてみてください!
Podcast 文字起こしはこちら
Discussion