はじめに
この記事ではGo言語のBun ORMを使用している際に、マルチテナントに対応する方法を説明する記事です。BunのGitHubにはexampleとしてmulti-tenantが示されていますが、ドキュメントには詳しくはされていません。
この記事ではGitHubの例をベースにマルチテナントを実装可能になることを目標とします。
解説
公式ではマルチテナント以外の部分も合わせてModel and query hooksとして示されています。
https://bun.uptrace.dev/guide/hooks.html
公式例
公式が示しているexampleはGitHubから見ることが可能です。
https://github.com/uptrace/bun/tree/master/example/multi-tenant
ただ、READMEを見ていただければ分かる通り、実装内容の解説は行われていません。コードの必要な箇所を抜粋しつつ解説を行っていきます。
Struct
例で示されてるStructは次のとおりです。
type Story struct {
ID int64 `bun:",pk,autoincrement"`
Title string
AuthorID int64
}
multi-tenant
実際にマルチテナントの実装箇所は次の部分です。
var _ bun.BeforeSelectHook = (*Story)(nil)
func (s *Story) BeforeSelect(ctx context.Context, query *bun.SelectQuery) error {
if id := ctx.Value("tenant_id"); id != nil {
query.Where("author_id = ?", id)
}
return nil
}
まずは、最初の行のvar _ bun.BeforeSelectHook = (*Story)(nil)
では、Story Struct
に対しメインのクエリを発行する前にクエリをフックさせるためのものです。
実装方法としては、実装したいStructに対し、メソッドとして実装します。引数にはcontext.Context
と*bun.SelectQuery
を指定します。返り値にはerror
を指定します。
あとは、実行したいクエリを*bun.SelectQuery
のWhereメソッドなどで実装していく形になります。
その他
今回の例ではSelectQueryですが、InsertQueryなどでも実装可能です。また、AfterSelect
など、メインのクエリの後に実装することも可能です。もちろん、どちらも実装することもできます。