Skip to content

Instantly share code, notes, and snippets.

@sekky0905
Last active November 29, 2019 03:59
Show Gist options
  • Save sekky0905/4332751e741935e641fe24c8c67dfdf4 to your computer and use it in GitHub Desktop.
Save sekky0905/4332751e741935e641fe24c8c67dfdf4 to your computer and use it in GitHub Desktop.
その設計、変更に強いですか?単体テストできますか?...そしてクリーンアーキテクチャ ref: https://qiita.com/Sekky0905/items/2436d669ff5d4491c527
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
}
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
}
// 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)
}
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