GoでDDD設計する際のrepositoryをどう定義するか
GoDDDでrepositoryを設計する際に色々考えたのでメモ
アーキテクチャ
この記事では、レイヤードアーキテクチャを使用します。
しかし正式なレイヤードでなく、以下のようにinfra層がdomain層に依存する形で設計します。
ディレクトリ構成
. ├── app ├── domain │ └── user │ ├── user.go │ └── user_repository.go ├── infra │ └── mongo │ └── user_repository.go └── ui
repository定義
今回は以下のUserエンティティを永続化することを想定して勧めていきます。
package user type User struct { ID uint Name string }
repositoryはinfra層とdomain層の2層に定義します。
domain層
domainディレクトリ配下に、
以下のようなRepositoryをインターフェースとして定義しておき、このRepositoryを実装すればUserエンティティが返ってくることを保証しておきます。
domain/user/user_repository.go
type UserRepository interface { Find(id int64) (User, error) FindAll(limit int) ([]User, error) }
infra層
infraディレクトリ配下に、
domain層のRepositoryインターフェースを実装したRepositoryを定義します。
infra/mongo/user_repository.go
package mongo type UserRepository struct { Context context.Context } func (r *UserRepository) Find(id int64) (user.User, error) { return r.find(r.Context, id) } func (r *UserRepository) FindAll(limit int) ([]user.User, error){ return r.findAll(r.Context, limit) }
repositoryの使用
package main import ( "fmt" "../domain/user" "../infra/mongo" ) func main() { // Userエンティティが取得できることを保証 var repo user.UserRepository repo = getUserRepoFromInfra() user, _ := repo.Find(1) fmt.Printf("type is %T", user) // => type is user.User } func getUserRepoFromInfra() user.UserRepository { return &mongo.UserRepository{} }
窓口の抽象化
以上のように、domain層とinfra層の窓口を抽象化しておくことでインフラの差し替えを容易にする事が出来ます。