「ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本」書籍がDDDを実践しはじめるのに分かりやすくとてもよいのですが、忘れてしまい何度か読み直しているのでまとめておきます。今回はリポジトリです。
ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
- 成瀬 允宣
- 翔泳社
- 価格¥1,760(2022/05/04 01:04時点)
- 発売日2020/02/13
- 商品ランキング3,867位
リポジトリとは
リポジトリはデータの保管庫です。直接データの永続化と再構築を行うのではなく、リポジトリを経由して行います。
リポジトリの責務
リポジトリの責務はドメインオブジェクトの永続化や再構築を行うことです。永続化するデータストアが RDB か NoSQL かファイルなのかドメインにとっては重要ではありません。
リポジトリのインターフェース
リポジトリはドメイン層にインターフェースで定義します。リポジトリの責務はあくまでもオブジェクトを永続化することです。
type Repository interface {
SaveUser(user *model.User) error
FindUserByName(name string) (*model.User, error)
}
SQL を利用したリポジトリを作成する
インターフェースをうまく活用することで、クラス上には具体的な永続化にまつわる処理を記述せずにデータストアにインスタンスを永続化可能になります。
リポジトリに定義されるふるまい
永続化のふるまいは永続化を行うオブジェクトを引数にとります。
良い例
type Repository interface {
SaveUser(user *model.User) error
DeleteUser(user *model.User) error
}
悪い例
type Repository interface {
UpdateUserByName(id, name string) error
UpdateUserByEmail(id, email string) error
UpdateUserByAddress(id, address string) error
}
テスト用のリポジトリを作成する
最後にテスト用のリポジトリを作成してDBユニットテストを実行してみます。
docker-compose.yml
test_mysql:
image: mysql:5.7.30
restart: always
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_USER: user
MYSQL_ROOT_PASSWORD: root
MYSQL_PASSWORD: password
MYSQL_DATABASE: go-ddd-test
ports:
- "13307:3306
infrastructure/repository/main_test.go
package repository
import (
"fmt"
"log"
"os"
"testing"
"time"
"github.com/joho/godotenv"
"github.com/taisa831/go-ddd/domain/model"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var rdb *gorm.DB
func TestMain(m *testing.M) {
err := godotenv.Load()
if err != nil {
log.Fatal(".env ファイルの読み込みに失敗しました。")
}
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Info,
IgnoreRecordNotFoundError: true,
Colorful: false,
},
)
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
os.Getenv("USERNAME"), os.Getenv("PASSWORD"), os.Getenv("DBHOST"), os.Getenv("DBPORT"), os.Getenv("SCHEMA"))
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
defer sqlDB.Close()
sqlDB.SetMaxOpenConns(151)
sqlDB.SetMaxIdleConns(100)
sqlDB.SetConnMaxLifetime(10 * time.Minute)
err = db.AutoMigrate(&model.User{})
if err != nil {
panic(err)
}
rdb = db
code := m.Run()
os.Exit(code)
}
func truncate(db *gorm.DB) {
db.Exec("truncate users")
}
infrastructure/repository/user_repository_test.go
package repository
import (
"reflect"
"testing"
"github.com/taisa831/go-ddd/domain/model"
"gorm.io/gorm"
)
func Test_dbRepository_FindUsers(t *testing.T) {
type fields struct {
db *gorm.DB
}
tests := []struct {
name string
fields fields
want []*model.User
wantErr bool
insertFixture func(db *gorm.DB)
}{
{
name: "FindUsers",
fields: fields{
db: rdb,
},
want: []*model.User{
{
ID: "u-1",
Name: "name-1",
},
},
wantErr: false,
insertFixture: func(db *gorm.DB) {
u := model.User{
ID: "u-1",
Name: "name-1",
}
if err := db.Create(&u).Error; err != nil {
t.Fatal(err)
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer truncate(tt.fields.db)
tt.insertFixture(tt.fields.db)
r := &dbRepository{
db: tt.fields.db,
}
got, err := r.FindUsers()
if (err != nil) != tt.wantErr {
t.Errorf("dbRepository.FindUsers() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("dbRepository.FindUsers() = %v, want %v", got, tt.want)
}
})
}
}
まとめ
- リポジトリを利用するとデータの永続化にまつわる処理が抽象化できる
- ドメインのルールに比べると、データストアが何であるかは些末な問題である
リポジトリについて簡単にまとめましたが、詳しくは下記書籍にてご確認ください。
ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
- 成瀬 允宣
- 翔泳社
- 価格¥1,760(2022/05/04 01:04時点)
- 発売日2020/02/13
- 商品ランキング3,867位
その他参考書籍
エリック・エヴァンスのドメイン駆動設計
- Eric Evans,和智右桂,牧野祐子,今関剛
- 翔泳社
- 価格¥5,148(2022/05/04 01:04時点)
- 発売日2011/04/08
- 商品ランキング56,430位
実践ドメイン駆動設計 (Object Oriented SELECTION)
- ヴォーン・ヴァーノン,髙木 正弘
- 翔泳社
- 価格¥5,720(2022/05/04 01:04時点)
- 発売日2015/03/16
- 商品ランキング109,829位