├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── cmd └── ddd_on_golang │ └── main.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── pkg ├── adapter │ └── controller │ │ ├── helpers │ │ └── converters.go │ │ ├── hunter │ │ ├── hunterAttackController.go │ │ ├── hunterGetMatrialController.go │ │ └── huntersController.go │ │ └── monster │ │ ├── monsterAttackController.go │ │ └── monstersController.go ├── domain │ ├── model │ │ ├── huntedMonsterMaterial.go │ │ ├── hunter.go │ │ ├── monster.go │ │ └── monsterMaterial.go │ ├── repository │ │ ├── hunterRepository.go │ │ └── monsterRepository.go │ └── service │ │ ├── hunterService.go │ │ └── monsterService.go ├── errors │ └── error.go ├── infrastructure │ ├── db.go │ ├── dto │ │ ├── huntedMonsterMaterial.go │ │ ├── hunter.go │ │ ├── monster.go │ │ └── monsterMaterial.go │ ├── queryImpl │ │ ├── hunter │ │ │ └── hunter.go │ │ └── monster │ │ │ └── monster.go │ ├── repositoryImpl │ │ ├── hunterRepositoryImpl.go │ │ └── monsterRepositoryImpl.go │ └── seeds │ │ └── seed.go ├── query │ ├── hunterQuery.go │ └── monsterQuery.go └── usecase │ ├── hunter │ ├── attackMonsterUseCase.go │ └── getMaterialFromMonsterUseCase.go │ └── monster │ └── attackHnterUseCase.go └── public └── images ├── access_flow.png ├── architecture.png ├── domain_model.png ├── external.png ├── favicon.png ├── get_material_from_monster.png ├── header-pattern.png ├── hunter_attacks_monster.png └── monster_attacks_hunter.png /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | bin/ 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.18 AS build 2 | 3 | RUN mkdir /ddd_on_golang 4 | WORKDIR /ddd_on_golang 5 | 6 | COPY go.mod . 7 | COPY go.sum . 8 | 9 | RUN go mod download 10 | 11 | COPY . . 12 | RUN cd cmd/ddd_on_golang && \ 13 | CGO_ENABLED=0 go build -o /bin/ddd_on_golang 14 | 15 | FROM scratch 16 | 17 | COPY --from=build /bin/ddd_on_golang /bin/ddd_on_golang 18 | 19 | CMD ["bin/ddd_on_golang"] 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .Phony:build 2 | build: 3 | cd cmd/ddd_on_golang && \ 4 | GOOS=linux go build -o ../../bin/main 5 | 6 | .Phony:attack_hunter 7 | attack_hunter: 8 | curl -X PUT \ 9 | -H 'Content-Type: application/json' \ 10 | -d '{"hunterId":1}' \ 11 | "http://localhost:8080/monsters/1/attack" | jq 12 | 13 | .Phony:attack_monster 14 | attack_monster: 15 | curl -X PUT \ 16 | -H 'Content-Type: application/json' \ 17 | -d '{"monsterId":1}' \ 18 | "http://localhost:8080/hunters/1/attack" | jq 19 | 20 | .Phony:find_all_hunters 21 | find_all_hunters: 22 | curl -X GET \ 23 | -H 'Content-Type: application/json' \ 24 | "http://localhost:8080/hunters/" | jq 25 | 26 | .Phony:find_all_monsters 27 | find_all_monsters: 28 | curl -X GET \ 29 | -H 'Content-Type: application/json' \ 30 | "http://localhost:8080/monsters/" | jq 31 | 32 | .Phony:find_monster 33 | find_monster: 34 | curl -X GET \ 35 | -H 'Content-Type: application/json' \ 36 | "http://localhost:8080/monsters/10" | jq 37 | 38 | .Phony:get_material_from_monster 39 | get_material_from_monster: 40 | curl -X POST \ 41 | -H 'Content-Type: application/json' \ 42 | -d '{"monsterId": "1"}' \ 43 | "http://localhost:8080/hunters/1/get_material_from_monster" | jq 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | :warning: 注意 :warning: 2 | 3 | ディレクトリ構成などはオレオレ流でありGolang Wayには全く則っていません。以下などを参考にしてください。 4 | 5 | https://github.com/golang-standards/project-layout 6 | 7 | 8 | --- 9 | 10 | # DDD on Golang Sample 11 | - [yu-croco/ddd_on_scala_sample](https://github.com/yu-croco/ddd_on_scala_sample) のGolangバージョン 12 | - Golang(Gin)を使い、なんちゃってモンハンの世界をDomain-Driven Designで実装している 13 | - アーキテクチャとしてのサンプルのため、ORM(Gorm)の使い方は割と適当... 14 | - Scala版とGolang版を実装してみての比較まとめはこちら: [ScalaとGolangでDDDを実装比較してみた](https://zenn.dev/nameless_gyoza/articles/e30d9e1283ab5b9576ff) 15 | 16 | ## 技術スタック 17 | - Golang: v1.15 18 | - Go modules 19 | - Gin: v1.6.3 20 | - Gorm: v1.9.16 21 | - Docker: 19.03.12 22 | - docker-compose: 1.26.2 23 | 24 | ## 構成 25 | ### 全体 26 | アプリケーション全体としては以下の構成となっており、いわゆるオニオンアーキテクチャの形式である。 27 | ![architecture](./public/images/architecture.png) 28 | 29 | 読み取りアクセス(GET)と書き込みアクセス(POST/PUT/DELETE)では処理フローを以下のように分けている(CQRS)。 30 | 31 | ![access flow](./public/images/access_flow.png) 32 | 33 | ### app配下の構成 34 | ドメインで使用するモデルはdomain配下に、DBとのアクセスで使用するモデルはDAOとしてinfrastructure配下にそれぞれ配置した。 35 | 同じような構成を2箇所で記述するのでコード量は増えるが、これによってDomain層のロジックが他の層に漏れ出すことを防げる。 36 | 37 | ``` 38 | pkg/ 39 | ├── adapter // 外部からのリクエスト処理 40 | │   └── controller 41 | ├── domain // ドメイン関連の処理 42 | │   ├── model 43 | │   ├── repository 44 | │   └── service 45 | ├── errors // アプリケーション全体のエラー処理 46 | │   └── error.go 47 | ├── infrastructure // DBアクセス処理 48 | │   ├── dao 49 | │   ├── db.go 50 | │   ├── queryImpl 51 | │   ├── repositoryImpl 52 | │   └── seeds 53 | ├── query // query processor 54 | │   ├── hunterQuery.go 55 | │   └── monsterQuery.go 56 | └── usecase // usecase(application) 57 | ├── hunter 58 | └── monster 59 | ``` 60 | 61 | ## ドメインモデル図 62 | このレポジトリで扱っているドメインモデル図は以下の通り。 63 | ![domain model](./public/images/domain_model.png) 64 | 65 | ## ユースケース図 66 | 67 | ### ハンター主体のケース 68 | - ハンターがモンスターを攻撃する 69 | - 確認コマンド: `make attack_monster` 70 | 71 | ![hunter_attacks_monster](./public/images/hunter_attacks_monster.png) 72 | 73 | - ハンターが倒したモンスターから素材を剥ぎ取る 74 | - 確認コマンド: `make get_material_frommonster` 75 | 76 | ![get_material_from_monster](./public/images/get_material_from_monster.png) 77 | 78 | ### モンスター主体のケース 79 | - モンスターがハンターを攻撃する 80 | - 確認コマンド: `make attack_hunter` 81 | 82 | ![monster_attacks_hunter](./public/images/monster_attacks_hunter.png) 83 | 84 | ## セットアップ 85 | - このレポジトリをgit cloneする 86 | - `docker-compose up`でAPIサーバーとDBが起動する 87 | - seedデータも投入される 88 | - Makefileにアクセス処理が入っているので、叩いて動作確認ができる 89 | 90 | ## 実装してみての考察 91 | Golangを用いてDDDをどこまでできたのか(できそうか)をまとめている。 92 | 前提として「一般的にDDDに向いていると言われているScalaで実装したものをGolangでもやってみた」というものであり、両者のパラダイムが異なることはある程度理解している。 93 | 94 | ### 意味のあると感じたこと 95 | - 階層/パッケージ分けにより責任の所在をある程度まとめられる 96 | - DDDというよりはオニオンアーキテクチャなどの観点かもしれないが、関心事の分離はアプリケーションの基本であると思うので重要なことに変わりはない 97 | - Repository層でのDI 98 | - interfaceとstructの組み合わせによるDIは、当初想定していた以上にキレイに実装できる感じだったので良かった 99 | - mockを使ったテストなどはしていないので、その点では未知数(たぶんうまくいける) 100 | 101 | ### 辛み 102 | - Genericを使った共通処理が作れないため記述量が増える 103 | - implicit classが恋しい 104 | - そもそも言語パラダイム的に兼ね備えていないので文句を言う方が不適切な気もするが... 105 | - パッケージ構成に慣れが必要 106 | - `import cycle not allowed` で怒られる... 107 | 108 | ## DDDらしさを引き出すためのTips 109 | ### 完全コンストラクタ 110 | Value Objectが何らかの存在条件を持っている場合(例えば `UserNameは5文字以上20文字以下のStringであること` など)には条件を満たないValue Objectの生成を防ぐために、Value Objectを生成する際に `必ず成功or失敗のどちらかとなる` ファクトリメソッドを用意すると便利(完全コンストラクタの実現)このレポジトリでは試験的にhunterId/monsterIdにその機能を取り入れた(それら以外のValue Objectは特別な条件を有していないため省略)。 111 | 112 | 懸念点としては、完全コンストラクタを採用したいオブジェクトが増えると記述するコード量が確実に増えることである。Scalaの型パラメータのように型を抽象化して共通化できないので、基盤の共通化が難しそうである。 113 | 114 | ```go 115 | // Domain層で完全コンストラクタのための初期化処理を記述 116 | func NewHunterId(id int) (*HunterId, *errors.AppError) { 117 | if id <= 0 { 118 | err := errors.NewAppError("HunterIdは1以上の値にしてください") 119 | return nil, &err 120 | } 121 | 122 | hunterId := HunterId(id) 123 | return &hunterId, nil 124 | } 125 | 126 | // Adapter層で使用する 127 | func (ctrl HunterAttackController) Update(c *gin.Context) { 128 | var monster model.Monster 129 | c.BindJSON(&monster) 130 | 131 | hunterId, hunterIdErr := model.NewHunterId(helpers.ConvertToInt(c.Param("id"))) 132 | if hunterIdErr.HasErrors() { 133 | helpers.Response(c, nil, hunterIdErr) 134 | } else { 135 | result, errs := hunter.AttackMonsterUseCase(*hunterId, monster.Id) 136 | helpers.Response(c, result, errs) 137 | } 138 | } 139 | ``` 140 | 141 | ### interfaceを使ったDI 142 | インターフェイスと実装を分ける手段として、RepositoryではDIコンテナが使用されることが一般的であると思う。GinではDIコンテナが提供されていないので、今回は自作している。 143 | 調べた範囲では以下のようなやり方が一般的のようである。インターフェイスとして持たせたい処理をinterfaceに記載し、それをstructに持たせることで実装を強制することができる。 144 | 145 | ```go 146 | // インターフェイス 147 | type MonsterRepository interface { 148 | FindById(id int) (*model.Monster, *errors.AppError) 149 | } 150 | 151 | // 以下、具体的な実装 152 | type MonsterRepositoryImpl struct{} 153 | 154 | func NewMonsterRepositoryImpl() repository.MonsterRepository { 155 | return &MonsterRepositoryImpl{} 156 | } 157 | 158 | func (repositoryImpl *MonsterRepositoryImpl) FindById(id int) (*model.Monster, *errors.AppError) { 159 | db := infrastructure.GetDB() 160 | var err errors.AppError 161 | monsterDao := dao.Monster{} 162 | 163 | if db.First(&monsterDao, dao.Monster{ID: uint(id)}).RecordNotFound() { 164 | err = notFoundMonsterError(id) 165 | return nil, &err 166 | } 167 | 168 | return monsterDao.ConvertToModel(), nil 169 | } 170 | ``` 171 | 172 | ### エラーの積み上げ 173 | Adapter層では1つのリクエスト処理に対して複数エラーが発生することがある(userNameとpasswordでそれぞれ何らかのエラーが発生した、など)。 174 | そういったケースに向けて、エラーを積み上げられるようにしておくと良いと思う。 175 | 176 | ```go 177 | type AppError struct { 178 | Errors []string `json:"errors"` 179 | } 180 | 181 | func NewAppError(message string) AppError { 182 | var errorResult []string 183 | 184 | err := errors.New(message) 185 | errorResult = append(errorResult, err.Error()) 186 | 187 | return AppError{Errors: errorResult} 188 | } 189 | 190 | func (appErr *AppError) Concat(other AppError) AppError { 191 | len := len(appErr.Errors) + len(other.Errors) 192 | errors := make([]string, len) 193 | 194 | errors = append(append(errors, appErr.Errors...), other.Errors...) 195 | 196 | return newAppErrors(errors) 197 | } 198 | 199 | func newAppErrors(messages []string) AppError { 200 | return AppError{Errors: messages} 201 | } 202 | 203 | func main() { 204 | err1 := NewAppError("ユーザー名は5文字以上20文字以内で記載してください") 205 | err2 := NewAppError("パスワードは8文字以上30文字以内で記載してください") 206 | 207 | erros := err1.Concat(err2) 208 | 209 | // これをresponseで返すと以下の感じになる 210 | // { 211 | // "errors": [ 212 | // "ユーザー名は5文字以上20文字以内で記載してください", 213 | // "パスワードは8文字以上30文字以内で記載してください" 214 | // ] 215 | // } 216 | } 217 | ``` 218 | 219 | ## 参考 220 | - [Clean Architecture in Go](https://medium.com/@hatajoe/clean-architecture-in-go-4030f11ec1b1) 221 | - [Practical Persistence in Go: Organising Database Access](https://www.alexedwards.net/blog/organising-database-access) 222 | - [今すぐ「レイヤードアーキテクチャ+DDD」を理解しよう。(golang)](https://qiita.com/tono-maron/items/345c433b86f74d314c8d) 223 | - [pospomeのサーバサイドアーキテクチャ3](https://booth.pm/ja/items/1578182) 224 | - [GOのORMを分かりやすくまとめてみた【GORM公式ドキュメントの焼き回し】](https://qiita.com/gold-kou/items/45a95d61d253184b0f33) 225 | - [Gin と GORM で作るオレオレ構成 API](https://qiita.com/Asuforce/items/0bde8cabb30ac094fcb4#controller-%E3%81%AB-action-%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B) 226 | -------------------------------------------------------------------------------- /cmd/ddd_on_golang/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "os" 7 | hunter2 "yu-croco/ddd_on_golang/pkg/adapter/controller/hunter" 8 | monster2 "yu-croco/ddd_on_golang/pkg/adapter/controller/monster" 9 | infrastructure2 "yu-croco/ddd_on_golang/pkg/infrastructure" 10 | ) 11 | 12 | func main() { 13 | db := infrastructure2.Init() 14 | defer db.Close() 15 | 16 | r := gin.Default() 17 | 18 | hunters := r.Group("/hunters") 19 | { 20 | huntersCtrl := hunter2.HuntersController{} 21 | hunters.GET("/:id", huntersCtrl.Show) 22 | hunters.GET("/", huntersCtrl.Index) 23 | 24 | hunterAttackCtrl := hunter2.HunterAttackController{} 25 | hunters.PUT("/:id/attack", hunterAttackCtrl.Update) 26 | 27 | hunterGetMaterialCtrl := hunter2.HunterGetMatrialController{} 28 | hunters.POST("/:id/get_material_from_monster", hunterGetMaterialCtrl.Update) 29 | } 30 | 31 | monsters := r.Group("/monsters") 32 | { 33 | monsterCtrl := monster2.Controller{} 34 | monsters.GET("/:id", monsterCtrl.Show) 35 | monsters.GET("/", monsterCtrl.Index) 36 | 37 | monsterAttackCtrl := monster2.MonsterAttackController{} 38 | monsters.PUT("/:id/attack", monsterAttackCtrl.Update) 39 | } 40 | 41 | if err := r.Run(); err != nil { 42 | fmt.Printf("error occured %v", err) 43 | os.Exit(1) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | api: 4 | build: . 5 | tty: true 6 | volumes: 7 | - .:/ddd_on_golang 8 | ports: 9 | - "8080:8080" 10 | depends_on: 11 | - db 12 | db: 13 | image: postgres:alpine 14 | environment: 15 | POSTGRES_USER: postgres 16 | POSTGRES_PASSWORD: ddd_on_golang 17 | POSTGRES_DB: ddd_on_golang 18 | ports: 19 | - 5432:5432 20 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module yu-croco/ddd_on_golang 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.6.3 7 | github.com/jinzhu/gorm v1.9.16 8 | ) 9 | 10 | require ( 11 | github.com/gin-contrib/sse v0.1.0 // indirect 12 | github.com/go-playground/locales v0.13.0 // indirect 13 | github.com/go-playground/universal-translator v0.17.0 // indirect 14 | github.com/go-playground/validator/v10 v10.3.0 // indirect 15 | github.com/golang/protobuf v1.4.2 // indirect 16 | github.com/jinzhu/inflection v1.0.0 // indirect 17 | github.com/json-iterator/go v1.1.10 // indirect 18 | github.com/leodido/go-urn v1.2.0 // indirect 19 | github.com/lib/pq v1.1.1 // indirect 20 | github.com/mattn/go-isatty v0.0.12 // indirect 21 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 22 | github.com/modern-go/reflect2 v1.0.1 // indirect 23 | github.com/ugorji/go/codec v1.1.7 // indirect 24 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect 25 | google.golang.org/protobuf v1.25.0 // indirect 26 | gopkg.in/yaml.v2 v2.3.0 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 4 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 5 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 6 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= 11 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 12 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 13 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 14 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 15 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 16 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 17 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 18 | github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= 19 | github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 20 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 21 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 22 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 23 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 24 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 25 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 26 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 27 | github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o= 28 | github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 29 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 30 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 31 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 32 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 33 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 34 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 35 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 36 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 37 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 38 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 39 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 40 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 41 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 42 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 43 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 44 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 45 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 46 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 47 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 48 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 49 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 50 | github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= 51 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 52 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 53 | github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= 54 | github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= 55 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 56 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 57 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= 58 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 59 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 60 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 61 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 62 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 63 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 64 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 65 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 66 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 67 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 68 | github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= 69 | github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= 70 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 71 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 72 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 73 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 74 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 75 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 76 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 77 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 78 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 79 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 80 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 81 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 82 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 83 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 84 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 85 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 86 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 87 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 88 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= 89 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 90 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 91 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 92 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 93 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 94 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 95 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 96 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 97 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 98 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 99 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 100 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 101 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 102 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 103 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 104 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 105 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 106 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 107 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 108 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 109 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 110 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 111 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc= 112 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 113 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 114 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 115 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 116 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 117 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 118 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 119 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 120 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 121 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 122 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 123 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 124 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 125 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 126 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 127 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 128 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 129 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 130 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 131 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 132 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 133 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 134 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 135 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 136 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 137 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 138 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= 139 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 140 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 141 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 142 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 143 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 144 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 145 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 146 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 147 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 148 | -------------------------------------------------------------------------------- /pkg/adapter/controller/helpers/converters.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strconv" 6 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 7 | ) 8 | 9 | // Note: 型はAPI仕様レベルで担保するので、ここではチェックしない 10 | func ConvertToInt(modelId string) int { 11 | id, _ := strconv.Atoi(modelId) 12 | return id 13 | } 14 | 15 | // Note: interface使うと型が壊れるのでアレだけど、そこはrequest testを作ることで担保する形でも良いかも 16 | func Response(c *gin.Context, result interface{}, err *errors2.AppError) { 17 | if err.HasErrors() { 18 | c.JSON(400, err) 19 | } else { 20 | c.JSON(200, result) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/adapter/controller/hunter/hunterAttackController.go: -------------------------------------------------------------------------------- 1 | package hunter 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | helpers2 "yu-croco/ddd_on_golang/pkg/adapter/controller/helpers" 6 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 7 | repositoryImpl2 "yu-croco/ddd_on_golang/pkg/infrastructure/repositoryImpl" 8 | "yu-croco/ddd_on_golang/pkg/usecase/hunter" 9 | ) 10 | 11 | type HunterAttackController struct{} 12 | 13 | func (ctrl HunterAttackController) Update(c *gin.Context) { 14 | var monster model2.Monster 15 | c.BindJSON(&monster) 16 | 17 | hunterId, hunterIdErr := model2.NewHunterId(helpers2.ConvertToInt(c.Param("id"))) 18 | if hunterIdErr.HasErrors() { 19 | helpers2.Response(c, nil, hunterIdErr) 20 | } else { 21 | hunterRepository := repositoryImpl2.NewHunterRepositoryImpl() 22 | monsterRepository := repositoryImpl2.NewMonsterRepositoryImpl() 23 | 24 | result, errs := hunter.NewAttackMonsterUseCaseImpl(*hunterId, monster.Id, hunterRepository, monsterRepository).Exec() 25 | helpers2.Response(c, result, errs) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pkg/adapter/controller/hunter/hunterGetMatrialController.go: -------------------------------------------------------------------------------- 1 | package hunter 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | helpers2 "yu-croco/ddd_on_golang/pkg/adapter/controller/helpers" 6 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 7 | repositoryImpl2 "yu-croco/ddd_on_golang/pkg/infrastructure/repositoryImpl" 8 | "yu-croco/ddd_on_golang/pkg/usecase/hunter" 9 | ) 10 | 11 | type HunterGetMatrialController struct{} 12 | 13 | func (ctrl HunterGetMatrialController) Update(c *gin.Context) { 14 | var monster model2.Monster 15 | c.BindJSON(&monster) 16 | hunterId, hunterIdErr := model2.NewHunterId(helpers2.ConvertToInt(c.Param("id"))) 17 | 18 | if hunterIdErr.HasErrors() { 19 | helpers2.Response(c, nil, hunterIdErr) 20 | } else { 21 | hunterRepository := repositoryImpl2.NewHunterRepositoryImpl() 22 | monsterRepository := repositoryImpl2.NewMonsterRepositoryImpl() 23 | 24 | result, errs := hunter.NewGetMaterialFromMonsterUseCaseImpl(*hunterId, monster.Id, hunterRepository, monsterRepository). 25 | Exec() 26 | helpers2.Response(c, result, errs) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/adapter/controller/hunter/huntersController.go: -------------------------------------------------------------------------------- 1 | package hunter 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | helpers2 "yu-croco/ddd_on_golang/pkg/adapter/controller/helpers" 6 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 7 | "yu-croco/ddd_on_golang/pkg/infrastructure/queryImpl/hunter" 8 | repositoryImpl2 "yu-croco/ddd_on_golang/pkg/infrastructure/repositoryImpl" 9 | ) 10 | 11 | type HuntersController struct{} 12 | 13 | func (ctrl HuntersController) Show(c *gin.Context) { 14 | hunterId, hunterIdErr := model2.NewHunterId(helpers2.ConvertToInt(c.Param("id"))) 15 | 16 | if hunterIdErr.HasErrors() { 17 | helpers2.Response(c, nil, hunterIdErr) 18 | } else { 19 | repo := repositoryImpl2.NewHunterRepositoryImpl() 20 | result, errs := repo.FindById(*hunterId) 21 | helpers2.Response(c, result, errs) 22 | } 23 | } 24 | 25 | func (ctrl HuntersController) Index(c *gin.Context) { 26 | result := hunter.NewHunterQueryImpl().FindAll() 27 | helpers2.Response(c, result, nil) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/adapter/controller/monster/monsterAttackController.go: -------------------------------------------------------------------------------- 1 | package monster 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | helpers2 "yu-croco/ddd_on_golang/pkg/adapter/controller/helpers" 6 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 7 | repositoryImpl2 "yu-croco/ddd_on_golang/pkg/infrastructure/repositoryImpl" 8 | "yu-croco/ddd_on_golang/pkg/usecase/monster" 9 | ) 10 | 11 | type MonsterAttackController struct{} 12 | 13 | func (ctrl MonsterAttackController) Update(c *gin.Context) { 14 | var hunter model2.Hunter 15 | c.BindJSON(&hunter) 16 | monsterId, monsterIdErr := model2.NewMonsterId(helpers2.ConvertToInt(c.Param("id"))) 17 | if monsterIdErr.HasErrors() { 18 | helpers2.Response(c, nil, monsterIdErr) 19 | } else { 20 | hunterRepository := repositoryImpl2.NewHunterRepositoryImpl() 21 | monsterRepository := repositoryImpl2.NewMonsterRepositoryImpl() 22 | 23 | result, errs := monster.NewAttackHunterUseCaseImpl(hunter.Id, *monsterId, hunterRepository, monsterRepository). 24 | Exec() 25 | helpers2.Response(c, result, errs) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /pkg/adapter/controller/monster/monstersController.go: -------------------------------------------------------------------------------- 1 | package monster 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | helpers2 "yu-croco/ddd_on_golang/pkg/adapter/controller/helpers" 6 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 7 | monster2 "yu-croco/ddd_on_golang/pkg/infrastructure/queryImpl/monster" 8 | repositoryImpl2 "yu-croco/ddd_on_golang/pkg/infrastructure/repositoryImpl" 9 | ) 10 | 11 | type Controller struct{} 12 | 13 | func (ctrl Controller) Show(c *gin.Context) { 14 | monsterId, monsterIfErr := model2.NewMonsterId(helpers2.ConvertToInt(c.Param("id"))) 15 | 16 | if monsterIfErr.HasErrors() { 17 | helpers2.Response(c, nil, monsterIfErr) 18 | } else { 19 | repo := repositoryImpl2.NewMonsterRepositoryImpl() 20 | result, errs := repo.FindById(*monsterId) 21 | 22 | helpers2.Response(c, result, errs) 23 | } 24 | } 25 | 26 | func (ctrl Controller) Index(c *gin.Context) { 27 | result := monster2.NewMonsterQueryImpl().FindAll() 28 | helpers2.Response(c, result, nil) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/domain/model/huntedMonsterMaterial.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type HuntedMonsterMaterial struct { 4 | Name HuntedMonsterMaterialName `json:"name"` 5 | Rarity HuntedMonsterMaterialRarity `json:"rarity"` 6 | } 7 | type HuntedMonsterMaterialName string 8 | type HuntedMonsterMaterialRarity int 9 | 10 | type HuntedMonsterMaterials []HuntedMonsterMaterial 11 | -------------------------------------------------------------------------------- /pkg/domain/model/hunter.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 5 | ) 6 | 7 | type Hunter struct { 8 | Id HunterId `json:"hunterId"` 9 | Name HunterName `json:"hunterName"` 10 | Life HunterLife `json:"life"` 11 | DefencePower HunterDefencePower `json:"defencePower"` 12 | OffensePower HunterOffensePower `json:"offensePower"` 13 | HuntedMaterials HuntedMonsterMaterials 14 | AttackDamage HunterAttackDamage `json:"attackDamage"` 15 | } 16 | 17 | // 完全コンストラクタのための初期化処理サンプル 18 | func NewHunterId(id int) (*HunterId, *errors2.AppError) { 19 | if id <= 0 { 20 | err := errors2.NewAppError("HunterIdは1以上の値にしてください") 21 | return nil, &err 22 | } 23 | 24 | hunterId := HunterId(id) 25 | return &hunterId, nil 26 | } 27 | 28 | type HunterId int 29 | type HunterName string 30 | type HunterLife int 31 | 32 | func (life HunterLife) Minus(damage MonsterOffensePower) HunterLife { 33 | return HunterLife(int(life) - int(damage)) 34 | } 35 | 36 | type HunterDefencePower int 37 | 38 | func (defence HunterDefencePower) BiggerOrSameThan(offense MonsterOffensePower) bool { 39 | return int(defence) >= int(offense) 40 | } 41 | 42 | type HunterOffensePower int 43 | 44 | func (offense HunterOffensePower) Twice() HunterOffensePower { 45 | return HunterOffensePower(int(offense) * 2) 46 | } 47 | func (offense HunterOffensePower) Minus(defence MonsterDefencePower) HunterOffensePower { 48 | return HunterOffensePower(int(offense) - int(defence)) 49 | } 50 | 51 | type HunterAttackDamage int 52 | 53 | type Hunters []Hunter 54 | 55 | func (hunter *Hunter) Attack(monster *Monster, damage HunterOffensePower) (*Monster, *errors2.AppError) { 56 | return monster.AttackedBy(damage) 57 | } 58 | 59 | func (hunter *Hunter) AttackedBy(givenDamage MonsterOffensePower) (*Hunter, *errors2.AppError) { 60 | var err errors2.AppError 61 | diff := hunter.Life.Minus(givenDamage) 62 | 63 | if hunter.Life == 0 { 64 | err = errors2.NewAppError("ハンターは既に倒れています") 65 | return nil, &err 66 | } 67 | 68 | if diff >= 0 { 69 | hunter.Life = diff 70 | } else { 71 | hunter.Life = HunterLife(0) 72 | } 73 | 74 | return hunter, &err 75 | } 76 | 77 | func (hunter *Hunter) GetMonsterMaterial(monster *Monster) (*MonsterMaterial, *errors2.AppError) { 78 | result, err := monster.TakenMaterial() 79 | 80 | return result, &err 81 | } 82 | -------------------------------------------------------------------------------- /pkg/domain/model/monster.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 5 | ) 6 | 7 | type Monster struct { 8 | Id MonsterId `json:"monsterId"` 9 | Name MonsterName `json:"monsterName"` 10 | Life MonsterLife `json:"life"` 11 | DefencePower MonsterDefencePower `json:"defencePower"` 12 | OffensePower MonsterOffensePower `json:"offensePower"` 13 | Materials MonsterMaterials 14 | AttackDamage MonsterAttackDamage `json:"attackDamage"` 15 | } 16 | 17 | // 完全コンストラクタのための初期化処理サンプル 18 | func NewMonsterId(id int) (*MonsterId, *errors2.AppError) { 19 | if id <= 0 { 20 | err := errors2.NewAppError("MonsterIdは1以上の値にしてください") 21 | return nil, &err 22 | } 23 | 24 | hunterId := MonsterId(id) 25 | return &hunterId, nil 26 | } 27 | 28 | type MonsterId int 29 | type MonsterName string 30 | type MonsterLife int 31 | 32 | func (life MonsterLife) Minus(damage HunterOffensePower) MonsterLife { 33 | return MonsterLife(int(life) - int(damage)) 34 | } 35 | 36 | type MonsterDefencePower int 37 | 38 | func (defence MonsterDefencePower) BiggerOrSameThan(offense HunterOffensePower) bool { 39 | return int(defence) >= int(offense) 40 | } 41 | 42 | type MonsterOffensePower int 43 | 44 | func (offense MonsterOffensePower) Twice() MonsterOffensePower { 45 | return MonsterOffensePower(int(offense) * 2) 46 | } 47 | func (offense MonsterOffensePower) Minus(defence HunterDefencePower) MonsterOffensePower { 48 | return MonsterOffensePower(int(offense) - int(defence)) 49 | } 50 | 51 | type MonsterAttackDamage int 52 | 53 | type Monsters []Monster 54 | 55 | func (monster *Monster) Attack(hunter *Hunter, damage MonsterOffensePower) (*Hunter, *errors2.AppError) { 56 | return hunter.AttackedBy(damage) 57 | } 58 | 59 | func (monster *Monster) AttackedBy(givenDamage HunterOffensePower) (*Monster, *errors2.AppError) { 60 | var err errors2.AppError 61 | diff := monster.Life.Minus(givenDamage) 62 | 63 | if monster.Life == 0 { 64 | err = errors2.NewAppError("このモンスターはすでに倒しています") 65 | return nil, &err 66 | } 67 | 68 | if diff >= 0 { 69 | monster.Life = diff 70 | } else { 71 | monster.Life = MonsterLife(0) 72 | } 73 | 74 | return monster, &err 75 | } 76 | 77 | func (monster *Monster) TakenMaterial() (*MonsterMaterial, errors2.AppError) { 78 | if monster.Life != 0 { 79 | return nil, errors2.NewAppError("このモンスターはまだ生きてるので剥ぎ取れません") 80 | } else { 81 | return &monster.Materials[0], errors2.AppError{} 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pkg/domain/model/monsterMaterial.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type MonsterMaterial struct { 4 | Name MonsterMaterialName `json:"name"` 5 | Rarity MonsterMaterialRarity `json:"rarity"` 6 | } 7 | 8 | type MonsterMaterialName string 9 | type MonsterMaterialRarity int 10 | 11 | type MonsterMaterials []MonsterMaterial 12 | -------------------------------------------------------------------------------- /pkg/domain/repository/hunterRepository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 6 | ) 7 | 8 | type HunterRepository interface { 9 | FindById(id model2.HunterId) (*model2.Hunter, *errors2.AppError) 10 | Update(hunter *model2.Hunter) (*model2.Hunter, *errors2.AppError) 11 | AddMonsterMaterial(hunter *model2.Hunter, material *model2.MonsterMaterial) *errors2.AppError 12 | } 13 | -------------------------------------------------------------------------------- /pkg/domain/repository/monsterRepository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 6 | ) 7 | 8 | type MonsterRepository interface { 9 | FindById(id model2.MonsterId) (*model2.Monster, *errors2.AppError) 10 | Update(monster *model2.Monster) (*model2.Monster, *errors2.AppError) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/domain/service/hunterService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | ) 6 | 7 | func CalculateAttackMonsterDamage(hunter *model2.Hunter, monster *model2.Monster) model2.HunterOffensePower { 8 | if monster.DefencePower.BiggerOrSameThan(hunter.OffensePower) { 9 | return hunter.OffensePower 10 | } else { 11 | return hunter.OffensePower.Twice().Minus(monster.DefencePower) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/domain/service/monsterService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | ) 6 | 7 | func CalculateAttackHunterDamage(monster *model2.Monster, hunter *model2.Hunter) model2.MonsterOffensePower { 8 | if hunter.DefencePower.BiggerOrSameThan(monster.OffensePower) { 9 | return monster.OffensePower 10 | } else { 11 | return monster.OffensePower.Twice().Minus(hunter.DefencePower) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/errors/error.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | // Note: 各層でのエラーハンドラーを作ると、層ごとで型が生まれてどこで変換するか面倒なので共通のエラー型を用意 4 | 5 | type AppError struct { 6 | Errors []string `json:"errors"` 7 | } 8 | 9 | func NewAppError(message string) AppError { 10 | errorResult := make([]string, 1) 11 | errorResult[0] = message 12 | 13 | return AppError{Errors: errorResult} 14 | } 15 | 16 | func (appErr *AppError) HasErrors() bool { 17 | if appErr == nil { 18 | return false 19 | } 20 | return len(appErr.Errors) >= 1 21 | } 22 | 23 | func (appErr *AppError) Concat(other *AppError) *AppError { 24 | if other.Errors == nil { 25 | return appErr 26 | } 27 | 28 | var errors []string 29 | errors = append(append(errors, appErr.Errors...), other.Errors...) 30 | 31 | return newAppErrors(errors) 32 | } 33 | 34 | func newAppErrors(messages []string) *AppError { 35 | return &AppError{Errors: messages} 36 | } 37 | -------------------------------------------------------------------------------- /pkg/infrastructure/db.go: -------------------------------------------------------------------------------- 1 | package infrastructure 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jinzhu/gorm" 6 | _ "github.com/jinzhu/gorm/dialects/postgres" 7 | "time" 8 | dto2 "yu-croco/ddd_on_golang/pkg/infrastructure/dto" 9 | seeds2 "yu-croco/ddd_on_golang/pkg/infrastructure/seeds" 10 | ) 11 | 12 | var ( 13 | db *gorm.DB 14 | err error 15 | ) 16 | 17 | func Init() *gorm.DB { 18 | fmt.Println("waiting to db start up....") 19 | time.Sleep(time.Second * 5) 20 | config := "host=db port=5432 user=postgres dbname=ddd_on_golang password=ddd_on_golang sslmode=disable" 21 | db, err = gorm.Open("postgres", config) 22 | if err != nil { 23 | fmt.Println("db init error: ", err) 24 | } 25 | 26 | autoMigrate() 27 | execSeeds() 28 | 29 | fmt.Println("[INFO]DB setup done!") 30 | return db 31 | } 32 | 33 | func GetDB() *gorm.DB { 34 | return db 35 | } 36 | 37 | func autoMigrate() { 38 | db. 39 | AutoMigrate(&dto2.Monster{}). 40 | AutoMigrate(&dto2.MonsterMaterial{}). 41 | AutoMigrate(&dto2.Hunter{}). 42 | AutoMigrate(&dto2.HuntedMonsterMaterial{}) 43 | } 44 | 45 | func execSeeds() { 46 | db. 47 | Create(&seeds2.MonsterSeed). 48 | Create(&seeds2.MonsterSeed2). 49 | Create(&seeds2.HunterSeed). 50 | Create(&seeds2.HunterSeed2) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/infrastructure/dto/huntedMonsterMaterial.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type HuntedMonsterMaterial struct { 4 | ID uint `json:"id" binding:"required"` 5 | MonsterMaterial MonsterMaterial `json:"-" binding:"required"` 6 | MonsterMaterialID uint `gorm:"not null" json:"monster_material_id"` 7 | } 8 | -------------------------------------------------------------------------------- /pkg/infrastructure/dto/hunter.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | ) 6 | 7 | type Hunter struct { 8 | ID uint `json:"id" binding:"required"` 9 | Name string 10 | Life int 11 | DefencePower int 12 | OffensePower int 13 | HuntedMaterials MonsterMaterials `gorm:"many2many:hunted_monster_materials"` 14 | } 15 | 16 | type Hunters []Hunter 17 | 18 | func (h *Hunter) ConvertToModel() *model2.Hunter { 19 | return &model2.Hunter{ 20 | Id: model2.HunterId(h.ID), 21 | Name: model2.HunterName(h.Name), 22 | Life: model2.HunterLife(h.Life), 23 | DefencePower: model2.HunterDefencePower(h.DefencePower), 24 | OffensePower: model2.HunterOffensePower(h.OffensePower), 25 | HuntedMaterials: model2.HuntedMonsterMaterials{}, 26 | AttackDamage: model2.HunterAttackDamage(0), 27 | } 28 | } 29 | 30 | func (hunters Hunters) ConvertToModel() *model2.Hunters { 31 | result := make(model2.Hunters, len(hunters)) 32 | 33 | for idx, hunter := range hunters { 34 | hunterModel := model2.Hunter{ 35 | Id: model2.HunterId(hunter.ID), 36 | Name: model2.HunterName(hunter.Name), 37 | Life: model2.HunterLife(hunter.Life), 38 | DefencePower: model2.HunterDefencePower(hunter.DefencePower), 39 | OffensePower: model2.HunterOffensePower(hunter.OffensePower), 40 | HuntedMaterials: convertMaterialRowToModel(hunter), 41 | AttackDamage: model2.HunterAttackDamage(0), 42 | } 43 | result[idx] = hunterModel 44 | } 45 | 46 | return &result 47 | } 48 | 49 | func convertMaterialRowToModel(hunter Hunter) model2.HuntedMonsterMaterials { 50 | materials := make(model2.HuntedMonsterMaterials, len(hunter.HuntedMaterials)) 51 | for idx2, material := range hunter.HuntedMaterials { 52 | materials[idx2] = model2.HuntedMonsterMaterial{ 53 | Name: model2.HuntedMonsterMaterialName(material.Name), 54 | Rarity: model2.HuntedMonsterMaterialRarity(material.Rarity), 55 | } 56 | } 57 | 58 | return materials 59 | } 60 | -------------------------------------------------------------------------------- /pkg/infrastructure/dto/monster.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | ) 6 | 7 | type Monster struct { 8 | ID uint `json:"id" binding:"required"` 9 | Name string 10 | Life int 11 | DefencePower int 12 | OffensePower int 13 | Materials MonsterMaterials 14 | } 15 | 16 | type Monsters []Monster 17 | 18 | func (m *Monster) ConvertToModel() *model2.Monster { 19 | var materials = make(model2.MonsterMaterials, len(m.Materials)) 20 | for idx, material := range m.Materials { 21 | materials[idx] = model2.MonsterMaterial{ 22 | Name: model2.MonsterMaterialName(material.Name), 23 | Rarity: model2.MonsterMaterialRarity(material.Rarity), 24 | } 25 | } 26 | 27 | return &model2.Monster{ 28 | Id: model2.MonsterId(m.ID), 29 | Name: model2.MonsterName(m.Name), 30 | Life: model2.MonsterLife(m.Life), 31 | DefencePower: model2.MonsterDefencePower(m.DefencePower), 32 | OffensePower: model2.MonsterOffensePower(m.OffensePower), 33 | Materials: materials, 34 | AttackDamage: model2.MonsterAttackDamage(0), 35 | } 36 | } 37 | 38 | func (monsters Monsters) ConvertToModel() *model2.Monsters { 39 | monsterModels := make(model2.Monsters, len(monsters)) 40 | 41 | for idx, monster := range monsters { 42 | model := model2.Monster{ 43 | Id: model2.MonsterId(monster.ID), 44 | Name: model2.MonsterName(monster.Name), 45 | Life: model2.MonsterLife(monster.Life), 46 | DefencePower: model2.MonsterDefencePower(monster.DefencePower), 47 | OffensePower: model2.MonsterOffensePower(monster.OffensePower), 48 | Materials: convertMonsterMaterialRowToModel(monster), 49 | AttackDamage: model2.MonsterAttackDamage(0), 50 | } 51 | monsterModels[idx] = model 52 | } 53 | return &monsterModels 54 | } 55 | 56 | func convertMonsterMaterialRowToModel(monster Monster) model2.MonsterMaterials { 57 | materials := make(model2.MonsterMaterials, len(monster.Materials)) 58 | for idx2, material := range monster.Materials { 59 | materials[idx2] = model2.MonsterMaterial{ 60 | Name: model2.MonsterMaterialName(material.Name), 61 | Rarity: model2.MonsterMaterialRarity(material.Rarity), 62 | } 63 | } 64 | 65 | return materials 66 | } 67 | -------------------------------------------------------------------------------- /pkg/infrastructure/dto/monsterMaterial.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type MonsterMaterial struct { 4 | ID uint 5 | Name string 6 | Rarity int 7 | Monster Monster `json:"-" binding:"required"` 8 | MonsterID uint `gorm:"not null" json:"monster_id"` 9 | Hunters Hunters `gorm:"many2many:hunted_monster_materials"` 10 | } 11 | 12 | type MonsterMaterials []MonsterMaterial 13 | -------------------------------------------------------------------------------- /pkg/infrastructure/queryImpl/hunter/hunter.go: -------------------------------------------------------------------------------- 1 | package hunter 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | infrastructure2 "yu-croco/ddd_on_golang/pkg/infrastructure" 6 | dto2 "yu-croco/ddd_on_golang/pkg/infrastructure/dto" 7 | query2 "yu-croco/ddd_on_golang/pkg/query" 8 | ) 9 | 10 | type hunterQueryImpl struct{} 11 | 12 | func NewHunterQueryImpl() query2.HunterQuery { 13 | return &hunterQueryImpl{} 14 | } 15 | 16 | func (repo hunterQueryImpl) FindAll() *model2.Hunters { 17 | db := infrastructure2.GetDB() 18 | hunterDaos := dto2.Hunters{} 19 | 20 | db.Preload("HuntedMaterials").Find(&hunterDaos) 21 | 22 | return hunterDaos.ConvertToModel() 23 | } 24 | -------------------------------------------------------------------------------- /pkg/infrastructure/queryImpl/monster/monster.go: -------------------------------------------------------------------------------- 1 | package monster 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | infrastructure2 "yu-croco/ddd_on_golang/pkg/infrastructure" 6 | dto2 "yu-croco/ddd_on_golang/pkg/infrastructure/dto" 7 | query2 "yu-croco/ddd_on_golang/pkg/query" 8 | ) 9 | 10 | type monsterQueryImpl struct{} 11 | 12 | func NewMonsterQueryImpl() query2.MonsterQuery { 13 | return &monsterQueryImpl{} 14 | } 15 | 16 | func (repo monsterQueryImpl) FindAll() *model2.Monsters { 17 | db := infrastructure2.GetDB() 18 | monsterDaos := dto2.Monsters{} 19 | 20 | db.Preload("Materials").Find(&monsterDaos) 21 | 22 | return monsterDaos.ConvertToModel() 23 | } 24 | -------------------------------------------------------------------------------- /pkg/infrastructure/repositoryImpl/hunterRepositoryImpl.go: -------------------------------------------------------------------------------- 1 | package repositoryImpl 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | repository2 "yu-croco/ddd_on_golang/pkg/domain/repository" 6 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 7 | infrastructure2 "yu-croco/ddd_on_golang/pkg/infrastructure" 8 | dto2 "yu-croco/ddd_on_golang/pkg/infrastructure/dto" 9 | ) 10 | 11 | type hunterRepositoryImpl struct{} 12 | 13 | func NewHunterRepositoryImpl() repository2.HunterRepository { 14 | return &hunterRepositoryImpl{} 15 | } 16 | 17 | func (repositoryImpl *hunterRepositoryImpl) FindById(id model2.HunterId) (*model2.Hunter, *errors2.AppError) { 18 | db := infrastructure2.GetDB() 19 | var err errors2.AppError 20 | hunterDao := dto2.Hunter{} 21 | 22 | if db.Find(&hunterDao, dto2.Hunter{ID: uint(id)}).RecordNotFound() { 23 | err = notFoundHunterError(id) 24 | return nil, &err 25 | } 26 | 27 | return hunterDao.ConvertToModel(), nil 28 | } 29 | 30 | func (repositoryImpl *hunterRepositoryImpl) Update(hunter *model2.Hunter) (*model2.Hunter, *errors2.AppError) { 31 | db := infrastructure2.GetDB() 32 | var err errors2.AppError 33 | hunterDao := dto2.Hunter{} 34 | 35 | if db.First(&hunterDao, dto2.Hunter{ID: uint(hunter.Id)}).RecordNotFound() { 36 | err = notFoundHunterError(hunter.Id) 37 | return nil, &err 38 | } 39 | 40 | hunterDao.Life = int(hunter.Life) 41 | db.Save(&hunterDao) 42 | 43 | return hunterDao.ConvertToModel(), nil 44 | } 45 | 46 | func (repositoryImpl *hunterRepositoryImpl) AddMonsterMaterial(hunter *model2.Hunter, material *model2.MonsterMaterial) *errors2.AppError { 47 | db := infrastructure2.GetDB() 48 | var err errors2.AppError 49 | hunterDao := dto2.Hunter{} 50 | 51 | if db.First(&hunterDao, dto2.Hunter{ID: uint(hunter.Id)}).RecordNotFound() { 52 | err = notFoundHunterError(hunter.Id) 53 | return &err 54 | } 55 | 56 | var materialDao dto2.MonsterMaterial 57 | if db.First(&materialDao, dto2.MonsterMaterial{Name: string(material.Name)}).RecordNotFound() { 58 | err = notFoundMaterialError(string(material.Name)) 59 | return &err 60 | } 61 | 62 | // ToDo: 同じハンターが同じ素材を複数取得できないので修正したい 63 | db.Model(&hunterDao).Association("HuntedMaterials").Append(materialDao) 64 | 65 | return nil 66 | } 67 | 68 | func notFoundHunterError(id model2.HunterId) errors2.AppError { 69 | return errors2.NewAppError("id " + string(id) + "のhunterは見つかりませんでした") 70 | } 71 | 72 | func notFoundMaterialError(material string) errors2.AppError { 73 | return errors2.NewAppError(material + "見つかりませんでした") 74 | } 75 | -------------------------------------------------------------------------------- /pkg/infrastructure/repositoryImpl/monsterRepositoryImpl.go: -------------------------------------------------------------------------------- 1 | package repositoryImpl 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | repository2 "yu-croco/ddd_on_golang/pkg/domain/repository" 6 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 7 | infrastructure2 "yu-croco/ddd_on_golang/pkg/infrastructure" 8 | dto2 "yu-croco/ddd_on_golang/pkg/infrastructure/dto" 9 | ) 10 | 11 | type monsterRepositoryImpl struct{} 12 | 13 | func NewMonsterRepositoryImpl() repository2.MonsterRepository { 14 | return &monsterRepositoryImpl{} 15 | } 16 | 17 | func (repositoryImpl *monsterRepositoryImpl) FindById(id model2.MonsterId) (*model2.Monster, *errors2.AppError) { 18 | db := infrastructure2.GetDB() 19 | var err errors2.AppError 20 | monsterDao := dto2.Monster{} 21 | 22 | if db.Preload("Materials").First(&monsterDao, dto2.Monster{ID: uint(id)}).RecordNotFound() { 23 | err = notFoundMonsterError(id) 24 | return nil, &err 25 | } 26 | 27 | return monsterDao.ConvertToModel(), nil 28 | } 29 | 30 | func (repositoryImpl *monsterRepositoryImpl) Update(monster *model2.Monster) (*model2.Monster, *errors2.AppError) { 31 | db := infrastructure2.GetDB() 32 | var err errors2.AppError 33 | monsterDao := dto2.Monster{} 34 | 35 | if db.First(&monsterDao, dto2.Monster{ID: uint(monster.Id)}).RecordNotFound() { 36 | err = notFoundMonsterError(monster.Id) 37 | return nil, &err 38 | } 39 | 40 | monsterDao.Life = int(monster.Life) 41 | 42 | db.Save(&monsterDao) 43 | return monsterDao.ConvertToModel(), nil 44 | } 45 | 46 | func notFoundMonsterError(id model2.MonsterId) errors2.AppError { 47 | return errors2.NewAppError("id " + string(id) + "のmonsterは見つかりませんでした") 48 | } 49 | -------------------------------------------------------------------------------- /pkg/infrastructure/seeds/seed.go: -------------------------------------------------------------------------------- 1 | package seeds 2 | 3 | import ( 4 | dto2 "yu-croco/ddd_on_golang/pkg/infrastructure/dto" 5 | ) 6 | 7 | var materials = dto2.MonsterMaterials{ 8 | { 9 | ID: 1, 10 | Name: "ランポスの皮", 11 | Rarity: 1, 12 | }, 13 | { 14 | ID: 2, 15 | Name: "ランポスの爪", 16 | Rarity: 1, 17 | }, 18 | } 19 | 20 | var materials2 = dto2.MonsterMaterials{ 21 | { 22 | ID: 3, 23 | Name: "アルビノの皮", 24 | Rarity: 2, 25 | }, 26 | { 27 | ID: 4, 28 | Name: "電気袋", 29 | Rarity: 2, 30 | }, 31 | } 32 | 33 | var MonsterSeed = dto2.Monster{ 34 | ID: 1, 35 | Name: "ランポス", 36 | Life: 150, 37 | DefencePower: 100, 38 | OffensePower: 110, 39 | Materials: materials, 40 | } 41 | 42 | var MonsterSeed2 = dto2.Monster{ 43 | ID: 2, 44 | Name: "フルフル", 45 | Life: 2300, 46 | DefencePower: 300, 47 | OffensePower: 250, 48 | Materials: materials2, 49 | } 50 | 51 | var HunterSeed = dto2.Hunter{ 52 | ID: 1, 53 | Name: "新米ハンター", 54 | Life: 150, 55 | DefencePower: 100, 56 | OffensePower: 110, 57 | HuntedMaterials: dto2.MonsterMaterials{}, 58 | } 59 | 60 | var HunterSeed2 = dto2.Hunter{ 61 | ID: 2, 62 | Name: "中級ハンター", 63 | Life: 450, 64 | DefencePower: 280, 65 | OffensePower: 310, 66 | HuntedMaterials: materials2, 67 | } 68 | -------------------------------------------------------------------------------- /pkg/query/hunterQuery.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | ) 6 | 7 | type HunterQuery interface { 8 | FindAll() *model2.Hunters 9 | } 10 | -------------------------------------------------------------------------------- /pkg/query/monsterQuery.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | ) 6 | 7 | type MonsterQuery interface { 8 | FindAll() *model2.Monsters 9 | } 10 | -------------------------------------------------------------------------------- /pkg/usecase/hunter/attackMonsterUseCase.go: -------------------------------------------------------------------------------- 1 | package hunter 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | repository2 "yu-croco/ddd_on_golang/pkg/domain/repository" 6 | service2 "yu-croco/ddd_on_golang/pkg/domain/service" 7 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 8 | ) 9 | 10 | type attackMonsterUseCaseImpl struct { 11 | HunterId model2.HunterId 12 | MonsterId model2.MonsterId 13 | HunterRepository repository2.HunterRepository 14 | MonsterRepository repository2.MonsterRepository 15 | } 16 | 17 | type attackMonsterUseCase interface { 18 | Exec() (*model2.Monster, *errors2.AppError) 19 | } 20 | 21 | func NewAttackMonsterUseCaseImpl(hunterId model2.HunterId, monsterId model2.MonsterId, 22 | hunterRepository repository2.HunterRepository, 23 | monsterRepository repository2.MonsterRepository) attackMonsterUseCase { 24 | 25 | return attackMonsterUseCaseImpl{ 26 | HunterId: hunterId, 27 | MonsterId: monsterId, 28 | HunterRepository: hunterRepository, 29 | MonsterRepository: monsterRepository, 30 | } 31 | } 32 | 33 | func (impl attackMonsterUseCaseImpl) Exec() (*model2.Monster, *errors2.AppError) { 34 | 35 | hunter, hunterFindErr := impl.HunterRepository.FindById(impl.HunterId) 36 | if hunterFindErr.HasErrors() { 37 | return nil, hunterFindErr 38 | } 39 | 40 | monster, monsterFindErr := impl.MonsterRepository.FindById(impl.MonsterId) 41 | if monsterFindErr.HasErrors() { 42 | return nil, monsterFindErr 43 | } 44 | 45 | hunterAttackDamage := service2.CalculateAttackMonsterDamage(hunter, monster) 46 | damagedMonster, attackErr := hunter.Attack(monster, hunterAttackDamage) 47 | 48 | if attackErr.HasErrors() { 49 | return nil, attackErr 50 | } 51 | 52 | updatedMonster, updateErr := impl.MonsterRepository.Update(damagedMonster) 53 | if updateErr.HasErrors() { 54 | return nil, updateErr 55 | } 56 | 57 | return updatedMonster, nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/usecase/hunter/getMaterialFromMonsterUseCase.go: -------------------------------------------------------------------------------- 1 | package hunter 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | repository2 "yu-croco/ddd_on_golang/pkg/domain/repository" 6 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 7 | ) 8 | 9 | type getMaterialFromMonsterUseCaseImpl struct { 10 | HunterId model2.HunterId 11 | MonsterId model2.MonsterId 12 | HunterRepository repository2.HunterRepository 13 | MonsterRepository repository2.MonsterRepository 14 | } 15 | type getMaterialFromMonsterUseCase interface { 16 | Exec() (*model2.MonsterMaterial, *errors2.AppError) 17 | } 18 | 19 | func NewGetMaterialFromMonsterUseCaseImpl(hunterId model2.HunterId, monsterId model2.MonsterId, 20 | hunterRepository repository2.HunterRepository, 21 | monsterRepository repository2.MonsterRepository) getMaterialFromMonsterUseCase { 22 | 23 | return getMaterialFromMonsterUseCaseImpl{ 24 | HunterId: hunterId, 25 | MonsterId: monsterId, 26 | HunterRepository: hunterRepository, 27 | MonsterRepository: monsterRepository, 28 | } 29 | } 30 | 31 | func (impl getMaterialFromMonsterUseCaseImpl) Exec() (*model2.MonsterMaterial, *errors2.AppError) { 32 | 33 | hunter, hunterFindErr := impl.HunterRepository.FindById(impl.HunterId) 34 | if hunterFindErr.HasErrors() { 35 | return nil, hunterFindErr 36 | } 37 | 38 | monster, monsterFindErr := impl.MonsterRepository.FindById(impl.MonsterId) 39 | if monsterFindErr.HasErrors() { 40 | return nil, monsterFindErr 41 | } 42 | 43 | takenMaterial, takeErr := hunter.GetMonsterMaterial(monster) 44 | if takeErr.HasErrors() { 45 | return nil, takeErr 46 | } 47 | 48 | impl.HunterRepository.AddMonsterMaterial(hunter, takenMaterial) 49 | 50 | return takenMaterial, nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/usecase/monster/attackHnterUseCase.go: -------------------------------------------------------------------------------- 1 | package monster 2 | 3 | import ( 4 | model2 "yu-croco/ddd_on_golang/pkg/domain/model" 5 | repository2 "yu-croco/ddd_on_golang/pkg/domain/repository" 6 | service2 "yu-croco/ddd_on_golang/pkg/domain/service" 7 | errors2 "yu-croco/ddd_on_golang/pkg/errors" 8 | ) 9 | 10 | type attackHunterUseCaseImpl struct { 11 | HunterId model2.HunterId 12 | MonsterId model2.MonsterId 13 | HunterRepository repository2.HunterRepository 14 | MonsterRepository repository2.MonsterRepository 15 | } 16 | 17 | type attackHunterUseCase interface { 18 | Exec() (*model2.Hunter, *errors2.AppError) 19 | } 20 | 21 | func NewAttackHunterUseCaseImpl(hunterId model2.HunterId, monsterId model2.MonsterId, 22 | hunterRepository repository2.HunterRepository, 23 | monsterRepository repository2.MonsterRepository) attackHunterUseCase { 24 | 25 | return attackHunterUseCaseImpl{ 26 | HunterId: hunterId, 27 | MonsterId: monsterId, 28 | HunterRepository: hunterRepository, 29 | MonsterRepository: monsterRepository, 30 | } 31 | } 32 | 33 | func (impl attackHunterUseCaseImpl) Exec() (*model2.Hunter, *errors2.AppError) { 34 | hunter, hunterFindErr := impl.HunterRepository.FindById(impl.HunterId) 35 | if hunterFindErr.HasErrors() { 36 | return nil, hunterFindErr 37 | } 38 | 39 | monster, monsterFindErr := impl.MonsterRepository.FindById(impl.MonsterId) 40 | if monsterFindErr.HasErrors() { 41 | return nil, monsterFindErr 42 | } 43 | 44 | monsterAttackDamage := service2.CalculateAttackHunterDamage(monster, hunter) 45 | damagedHunter, attackErr := monster.Attack(hunter, monsterAttackDamage) 46 | if attackErr.HasErrors() { 47 | return nil, attackErr 48 | } 49 | updatedHunter, updateErr := impl.HunterRepository.Update(damagedHunter) 50 | if updateErr.HasErrors() { 51 | return nil, updateErr 52 | } 53 | return updatedHunter, nil 54 | } 55 | -------------------------------------------------------------------------------- /public/images/access_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/access_flow.png -------------------------------------------------------------------------------- /public/images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/architecture.png -------------------------------------------------------------------------------- /public/images/domain_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/domain_model.png -------------------------------------------------------------------------------- /public/images/external.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/external.png -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/favicon.png -------------------------------------------------------------------------------- /public/images/get_material_from_monster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/get_material_from_monster.png -------------------------------------------------------------------------------- /public/images/header-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/header-pattern.png -------------------------------------------------------------------------------- /public/images/hunter_attacks_monster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/hunter_attacks_monster.png -------------------------------------------------------------------------------- /public/images/monster_attacks_hunter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yu-croco/ddd_on_golang_sample/e4bb30e68305fadaf89cd75b8a4c77c959c9229b/public/images/monster_attacks_hunter.png --------------------------------------------------------------------------------