🌟
Go + Bun + PostgreSQLの構成をつくる
背景
個人プロジェクトでPostgreSQLのクエリのために生SQLを用いているが、うっかりSQLインジェクションの脆弱性を生んだりしそうで怖いので、Bunを導入する。
とりあえず動くようにする
リポジトリの実装として与えていたdbを*sql.DB
から*bun.DB
に置き換える。
*sql.DB
をラップするだけ。このとき、PostgreSQLを使うためには以下のようにpgdialect.New()
を呼び出す。
databaseUrl := fmt.Sprintf("postgresql://%s:5432/%s?user=%s&password=%s&sslmode=disable", dbHost, dbName, dbUser, dbPass)
- db, err = sql.Open("pgx", databaseUrl)
+ sqldb, err := sql.Open("pgx", databaseUrl)
if err != nil {
return err
}
defer sqldb.Close()
+ db = bun.NewDB(sqldb, pgdialect.New())
+ defer db.Close()
参考: https://bun.uptrace.dev/postgres/
生SQLのプレースホルダを$1
から?
に変更する
func (r UserRepository) Find(ctx context.Context, id string) (*domain.User, error) {
- const query = "SELECT id, display_name FROM users.users WHERE id = $1"
+ const query = "SELECT id, display_name FROM users.users WHERE id = ?"
user := &domain.User{}
err := r.db.QueryRowContext(ctx, query, id).Scan(&user.ID, &user.DisplayName)
if err != nil {
return nil, errors.Wrap(err, "scanning user")
}
return user, nil
}
SQLをクエリビルダで組み立てる
公式ドキュメントのGetting Startedに基づいて進めていく
+type User struct {
+ bun.BaseModel `bun:"table:users.users,alias:u"`
+
+ ID string `bun:"id,pk"`
+ DisplayName string `bun:"display_name,notnull"`
+}
+
- クエリを置き換える
func (r UserRepository) Find(ctx context.Context, id string) (*domain.User, error) {
- const query = "SELECT id, display_name FROM users.users WHERE id = ?"
+ user := new(User)
+ err := r.db.NewSelect().Model(user).Where("id = ?", id).Scan(ctx)
- user := &domain.User{}
- err := r.db.QueryRowContext(ctx, query, id).Scan(&user.ID, &user.DisplayName)
if err != nil {
return nil, errors.Wrap(err, "scanning user")
}
- return user, nil
+
+ return &domain.User{
+ ID: user.ID,
+ DisplayName: user.DisplayName,
+ }, nil
}
これで、問題なく生SQLを置き換えられた。
疑問
流石に構造体の詰め替えが多すぎる気がするが、これはよくあることなんだろうか。
現時点で、User
に対して以下のような構造体がある。
- gRPCのリクエストの形式(UI)
- ユースケース層のモデル(ユースケース)
- ドメインモデル(ドメイン)
- DBのモデル(リポジトリ))
この辺の疑問に答える本として手を動かしてわかるクリーンアーキテクチャ ヘキサゴナルアーキテクチャによるクリーンなアプリケーション開発
が紹介されていたが、まだ中身は見られていない。。。
Discussion