爆速でGo!

GoGo!

GoでDDD設計する際のrepositoryをどう定義するか

GoDDDでrepositoryを設計する際に色々考えたのでメモ

アーキテクチャ

この記事では、レイヤードアーキテクチャを使用します。
しかし正式なレイヤードでなく、以下のようにinfra層がdomain層に依存する形で設計します。

f:id:NakaWatch:20180210164321p:plain

ディレクトリ構成

.
├── 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層の窓口を抽象化しておくことでインフラの差し替えを容易にする事が出来ます。