Last active
November 29, 2019 03:59
-
-
Save sekky0905/4332751e741935e641fe24c8c67dfdf4 to your computer and use it in GitHub Desktop.
その設計、変更に強いですか?単体テストできますか?...そしてクリーンアーキテクチャ ref: https://qiita.com/Sekky0905/items/2436d669ff5d4491c527
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package rdb | |
import ( | |
"context" | |
"fmt" | |
"github.com/SekiguchiKai/clean-architecture-with-go/server/domain/model" | |
"github.com/SekiguchiKai/clean-architecture-with-go/server/domain/repository" | |
"github.com/pkg/errors" | |
) | |
// ProgrammingLangDAO は、ProgrammingLangのDAO。 | |
type ProgrammingLangDAO struct { | |
SQLManager SQLManagerInterface | |
} | |
// NewProgrammingLangDAO は、ProgrammingLangDAO生成して返す。 | |
func NewProgrammingLangDAO(manager SQLManagerInterface) repository.ProgrammingLangRepository { | |
fmt.Printf("NewProgrammingLangDAO") | |
return &ProgrammingLangDAO{ | |
SQLManager: manager, | |
} | |
} | |
// ErrorMsg は、エラー文を生成し、返す。 | |
func (dao *ProgrammingLangDAO) ErrorMsg(method string, err error) error { | |
return &model.DBError{ | |
ModelName: model.ModelNameProgrammingLang, | |
DBMethod: method, | |
Detail: err.Error(), | |
} | |
} | |
// Create は、レコードを1件生成する。 | |
func (dao *ProgrammingLangDAO) Create(ctx context.Context, lang *model.ProgrammingLang) (*model.ProgrammingLang, error) { | |
query := "INSERT INTO programming_langs (name, feature, created_at, updated_at) VALUES (?, ?, ?, ?)" | |
stmt, err := dao.SQLManager.PrepareContext(ctx, query) | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodCreate, err) | |
} | |
defer stmt.Close() | |
result, err := stmt.ExecContext(ctx, lang.Name, lang.Feature, lang.CreatedAt, lang.UpdatedAt) | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodCreate, err) | |
} | |
affect, err := result.RowsAffected() | |
if affect != 1 { | |
err = fmt.Errorf("%s: %d ", TotalAffected, affect) | |
return nil, dao.ErrorMsg(model.DBMethodUpdate, err) | |
} | |
id, err := result.LastInsertId() | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodCreate, err) | |
} | |
lang.ID = int(id) | |
return lang, nil | |
} | |
// List は、レコードの一覧を取得して返す。 | |
func (dao *ProgrammingLangDAO) List(ctx context.Context, limit int) ([]*model.ProgrammingLang, error) { | |
query := "SELECT id, name, feature, created_at, updated_at FROM programming_langs ORDER BY name LIMIT ?" | |
langSlice, err := dao.list(ctx, query, limit) | |
if len(langSlice) == 0 { | |
return nil, &model.NoSuchDataError{ | |
ModelName: model.ModelNameProgrammingLang, | |
} | |
} | |
if err != nil { | |
return nil, errors.WithStack(err) | |
} | |
return langSlice, nil | |
} | |
// Read は、レコードを1件取得して返す。 | |
func (dao *ProgrammingLangDAO) Read(ctx context.Context, id int) (*model.ProgrammingLang, error) { | |
query := "SELECT id, name, feature, created_at, updated_at FROM programming_langs WHERE ID=?" | |
langSlice, err := dao.list(ctx, query, id) | |
if len(langSlice) == 0 { | |
return nil, &model.NoSuchDataError{ | |
ID: id, | |
ModelName: model.ModelNameProgrammingLang, | |
} | |
} | |
if err != nil { | |
return nil, errors.WithStack(err) | |
} | |
return langSlice[0], nil | |
} | |
// ReadByName は、指定したNameを保持するレコードを1返す。 | |
func (dao *ProgrammingLangDAO) ReadByName(ctx context.Context, name string) (*model.ProgrammingLang, error) { | |
query := "SELECT id, name, feature, created_at, updated_at FROM programming_langs WHERE name=? ORDER BY name LIMIT ?" | |
langSlice, err := dao.list(ctx, query, name, 1) | |
if len(langSlice) == 0 { | |
return nil, &model.NoSuchDataError{ | |
Name: name, | |
ModelName: model.ModelNameProgrammingLang, | |
} | |
} | |
if err != nil { | |
return nil, errors.WithStack(err) | |
} | |
return langSlice[0], nil | |
} | |
// list は、レコードの一覧を取得して返す。 | |
func (dao *ProgrammingLangDAO) list(ctx context.Context, query string, args ...interface{}) ([]*model.ProgrammingLang, error) { | |
stmt, err := dao.SQLManager.PrepareContext(ctx, query) | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodList, err) | |
} | |
defer stmt.Close() | |
rows, err := stmt.QueryContext(ctx, args...) | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodList, err) | |
} | |
defer rows.Close() | |
langSlice := make([]*model.ProgrammingLang, 0) | |
for rows.Next() { | |
lang := &model.ProgrammingLang{} | |
err = rows.Scan( | |
&lang.ID, | |
&lang.Name, | |
&lang.Feature, | |
&lang.CreatedAt, | |
&lang.UpdatedAt, | |
) | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodList, err) | |
} | |
langSlice = append(langSlice, lang) | |
} | |
return langSlice, nil | |
} | |
// Update は、レコードを1件更新する。 | |
func (dao *ProgrammingLangDAO) Update(ctx context.Context, lang *model.ProgrammingLang) (*model.ProgrammingLang, error) { | |
query := "UPDATE programming_langs SET name=?, feature=?, created_at=?, updated_at=? WHERE id=?" | |
stmt, err := dao.SQLManager.PrepareContext(ctx, query) | |
defer stmt.Close() | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodUpdate, err) | |
} | |
result, err := stmt.ExecContext(ctx, lang.Name, lang.Feature, lang.CreatedAt, lang.UpdatedAt, lang.ID) | |
if err != nil { | |
return nil, dao.ErrorMsg(model.DBMethodUpdate, err) | |
} | |
affect, err := result.RowsAffected() | |
if affect != 1 { | |
err = fmt.Errorf("%s: %d ", TotalAffected, affect) | |
return nil, dao.ErrorMsg(model.DBMethodUpdate, err) | |
} | |
return lang, nil | |
} | |
// Delete は、レコードを1件削除する。 | |
func (dao *ProgrammingLangDAO) Delete(ctx context.Context, id int) error { | |
query := "DELETE FROM programming_langs WHERE id=?" | |
stmt, err := dao.SQLManager.PrepareContext(ctx, query) | |
if err != nil { | |
return dao.ErrorMsg(model.DBMethodDelete, err) | |
} | |
defer stmt.Close() | |
result, err := stmt.ExecContext(ctx, id) | |
if err != nil { | |
return dao.ErrorMsg(model.DBMethodDelete, err) | |
} | |
affect, err := result.RowsAffected() | |
if err != nil { | |
return dao.ErrorMsg(model.DBMethodDelete, err) | |
} | |
if affect != 1 { | |
err = fmt.Errorf("%s: %d ", TotalAffected, affect) | |
return dao.ErrorMsg(model.DBMethodDelete, err) | |
} | |
return nil | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package repository | |
import ( | |
"context" | |
"github.com/SekiguchiKai/clean-architecture-with-go/server/domain/model" | |
) | |
// ProgrammingLangRepository は、ProgrammingLangのRepository。 | |
type ProgrammingLangRepository interface { | |
List(ctx context.Context, limit int) ([]*model.ProgrammingLang, error) | |
Create(ctx context.Context, lang *model.ProgrammingLang) (*model.ProgrammingLang, error) | |
Read(ctx context.Context, id int) (*model.ProgrammingLang, error) | |
ReadByName(ctx context.Context, name string) (*model.ProgrammingLang, error) | |
Update(ctx context.Context, lang *model.ProgrammingLang) (*model.ProgrammingLang, error) | |
Delete(ctx context.Context, id int) error | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Code generated by MockGen. DO NOT EDIT. | |
// Source: domain/repository/programming_lang_repository.go | |
// Package mock_repository is a generated GoMock package. | |
package mock_repository | |
import ( | |
context "context" | |
model "github.com/SekiguchiKai/clean-architecture-with-go/server/domain/model" | |
gomock "github.com/golang/mock/gomock" | |
reflect "reflect" | |
) | |
// MockProgrammingLangRepository is a mock of ProgrammingLangRepository interface | |
type MockProgrammingLangRepository struct { | |
ctrl *gomock.Controller | |
recorder *MockProgrammingLangRepositoryMockRecorder | |
} | |
// MockProgrammingLangRepositoryMockRecorder is the mock recorder for MockProgrammingLangRepository | |
type MockProgrammingLangRepositoryMockRecorder struct { | |
mock *MockProgrammingLangRepository | |
} | |
// NewMockProgrammingLangRepository creates a new mock instance | |
func NewMockProgrammingLangRepository(ctrl *gomock.Controller) *MockProgrammingLangRepository { | |
mock := &MockProgrammingLangRepository{ctrl: ctrl} | |
mock.recorder = &MockProgrammingLangRepositoryMockRecorder{mock} | |
return mock | |
} | |
// EXPECT returns an object that allows the caller to indicate expected use | |
func (m *MockProgrammingLangRepository) EXPECT() *MockProgrammingLangRepositoryMockRecorder { | |
return m.recorder | |
} | |
// List mocks base method | |
func (m *MockProgrammingLangRepository) List(ctx context.Context, limit int) ([]*model.ProgrammingLang, error) { | |
ret := m.ctrl.Call(m, "List", ctx, limit) | |
ret0, _ := ret[0].([]*model.ProgrammingLang) | |
ret1, _ := ret[1].(error) | |
return ret0, ret1 | |
} | |
// List indicates an expected call of List | |
func (mr *MockProgrammingLangRepositoryMockRecorder) List(ctx, limit interface{}) *gomock.Call { | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockProgrammingLangRepository)(nil).List), ctx, limit) | |
} | |
// Create mocks base method | |
func (m *MockProgrammingLangRepository) Create(ctx context.Context, lang *model.ProgrammingLang) (*model.ProgrammingLang, error) { | |
ret := m.ctrl.Call(m, "Create", ctx, lang) | |
ret0, _ := ret[0].(*model.ProgrammingLang) | |
ret1, _ := ret[1].(error) | |
return ret0, ret1 | |
} | |
// Create indicates an expected call of Create | |
func (mr *MockProgrammingLangRepositoryMockRecorder) Create(ctx, lang interface{}) *gomock.Call { | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockProgrammingLangRepository)(nil).Create), ctx, lang) | |
} | |
// Read mocks base method | |
func (m *MockProgrammingLangRepository) Read(ctx context.Context, id int) (*model.ProgrammingLang, error) { | |
ret := m.ctrl.Call(m, "Read", ctx, id) | |
ret0, _ := ret[0].(*model.ProgrammingLang) | |
ret1, _ := ret[1].(error) | |
return ret0, ret1 | |
} | |
// Read indicates an expected call of Read | |
func (mr *MockProgrammingLangRepositoryMockRecorder) Read(ctx, id interface{}) *gomock.Call { | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockProgrammingLangRepository)(nil).Read), ctx, id) | |
} | |
// ReadByName mocks base method | |
func (m *MockProgrammingLangRepository) ReadByName(ctx context.Context, name string) (*model.ProgrammingLang, error) { | |
ret := m.ctrl.Call(m, "ReadByName", ctx, name) | |
ret0, _ := ret[0].(*model.ProgrammingLang) | |
ret1, _ := ret[1].(error) | |
return ret0, ret1 | |
} | |
// ReadByName indicates an expected call of ReadByName | |
func (mr *MockProgrammingLangRepositoryMockRecorder) ReadByName(ctx, name interface{}) *gomock.Call { | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadByName", reflect.TypeOf((*MockProgrammingLangRepository)(nil).ReadByName), ctx, name) | |
} | |
// Update mocks base method | |
func (m *MockProgrammingLangRepository) Update(ctx context.Context, lang *model.ProgrammingLang) (*model.ProgrammingLang, error) { | |
ret := m.ctrl.Call(m, "Update", ctx, lang) | |
ret0, _ := ret[0].(*model.ProgrammingLang) | |
ret1, _ := ret[1].(error) | |
return ret0, ret1 | |
} | |
// Update indicates an expected call of Update | |
func (mr *MockProgrammingLangRepositoryMockRecorder) Update(ctx, lang interface{}) *gomock.Call { | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProgrammingLangRepository)(nil).Update), ctx, lang) | |
} | |
// Delete mocks base method | |
func (m *MockProgrammingLangRepository) Delete(ctx context.Context, id int) error { | |
ret := m.ctrl.Call(m, "Delete", ctx, id) | |
ret0, _ := ret[0].(error) | |
return ret0 | |
} | |
// Delete indicates an expected call of Delete | |
func (mr *MockProgrammingLangRepositoryMockRecorder) Delete(ctx, id interface{}) *gomock.Call { | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProgrammingLangRepository)(nil).Delete), ctx, id) | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package usecase | |
import ( | |
"context" | |
"time" | |
"github.com/SekiguchiKai/clean-architecture-with-go/server/domain/model" | |
"github.com/SekiguchiKai/clean-architecture-with-go/server/domain/repository" | |
"github.com/SekiguchiKai/clean-architecture-with-go/server/usecase/input" | |
"github.com/pkg/errors" | |
) | |
// ProgrammingLangUseCase は、ProgrammingLangのUseCase。 | |
type ProgrammingLangUseCase struct { | |
Repo repository.ProgrammingLangRepository | |
} | |
// NewProgrammingLangUseCase は、ProgrammingLangUseCaseを生成し、返す。 | |
func NewProgrammingLangUseCase(repo repository.ProgrammingLangRepository) input.ProgrammingLangInputPort { | |
return &ProgrammingLangUseCase{ | |
Repo: repo, | |
} | |
} | |
// List は、ProgrammingLangの一覧を返す。 | |
func (u *ProgrammingLangUseCase) List(ctx context.Context, limit int) ([]*model.ProgrammingLang, error) { | |
return u.Repo.List(ctx, limit) | |
} | |
// Get は、ProgrammingLang1件返す。 | |
func (u *ProgrammingLangUseCase) Get(ctx context.Context, id int) (*model.ProgrammingLang, error) { | |
return u.Repo.Read(ctx, id) | |
} | |
// Create は、ProgrammingLangを生成する。 | |
func (u *ProgrammingLangUseCase) Create(ctx context.Context, param *model.ProgrammingLang) (*model.ProgrammingLang, error) { | |
lang, err := u.Repo.ReadByName(ctx, param.Name) | |
if lang != nil { | |
return nil, &model.AlreadyExistError{ | |
ID: lang.ID, | |
Name: lang.Name, | |
ModelName: model.ModelNameProgrammingLang, | |
} | |
} | |
if _, ok := errors.Cause(err).(*model.NoSuchDataError); !ok { | |
return nil, errors.WithStack(err) | |
} | |
param.CreatedAt = time.Now().UTC() | |
param.UpdatedAt = time.Now().UTC() | |
lang, err = u.Repo.Create(ctx, param) | |
if err != nil { | |
return nil, errors.WithStack(err) | |
} | |
return lang, nil | |
} | |
// Update は、ProgrammingLangを更新する。 | |
func (u *ProgrammingLangUseCase) Update(ctx context.Context, id int, param *model.ProgrammingLang) (*model.ProgrammingLang, error) { | |
lang, err := u.Repo.Read(ctx, id) | |
if lang == nil { | |
return nil, &model.NoSuchDataError{ | |
ID: id, | |
Name: param.Name, | |
ModelName: model.ModelNameProgrammingLang, | |
} | |
} else if err != nil { | |
return nil, errors.WithStack(err) | |
} | |
lang.ID = id | |
lang.Name = param.Name | |
lang.Feature = param.Feature | |
lang.UpdatedAt = time.Now().UTC() | |
return u.Repo.Update(ctx, lang) | |
} | |
// Delete は、ProgrammingLangを削除する。 | |
func (u *ProgrammingLangUseCase) Delete(ctx context.Context, id int) error { | |
lang, err := u.Repo.Read(ctx, id) | |
if lang == nil { | |
return &model.NoSuchDataError{ | |
ID: id, | |
ModelName: model.ModelNameProgrammingLang, | |
} | |
} else if err != nil { | |
return errors.WithStack(err) | |
} | |
return u.Repo.Delete(ctx, id) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment