LoginSignup
1
4

More than 3 years have passed since last update.

GoでClean ArchitectureでのAPIを実装したときにハマったInterfaceの呼び出し方

Last updated at Posted at 2020-03-08

はじめに

今回はGoとClean Architecutureの復習を兼ねて、ゼロからAPIを実装したときに詰まった部分を備忘のためにここに記載します。Clean Architectureならびに利用したフレームワークのbeego自体については、あまりふれないので参考文献をご覧ください。

詰まった部分

Interface層であるControllerで、構造体を作成する際にどうやってInterface(継承)をわたせばいいのかわかりませんでした。
具体的には、関数NewStatusControllerに渡す引数appConfigInterfaceの構造体と具体的な値です。
渡すべき引数は、作成したInterface(継承)だと思っていたのですが、Interface(継承)を渡すということに、直感的に「?」が生まれました。

status.go
package controllers

import (
    "yamamon/flathand-bookmark/interfaces/inside"
    "yamamon/flathand-bookmark/usecase"
)

var statusController *StatusController

type StatusController struct {
    BaseController
    Interactor usecase.ConfigInteractor
}

func NewStatusController(appConfigInterface inside.AppConfig) *StatusController {
    return &StatusController{
        Interactor:     usecase.ConfigInteractor{
            ConfigRepository: &inside.ConfigRepository{
                AppConfig: appConfigInterface,
            },
        },
    }
}

// @router /_me [get]
func (controller *StatusController) Get() {
    appConfig := controller.Interactor.ShowAppConfig()
    controller.ServeResponseJSON(appConfig)
}

appConfigInterfaceの構造体と値

appConfigInterfaceの構造体については、やはりInterface(継承)でしたが、具体的な値についてはInterface(継承)を実装した構造体自身の値(ポインタ)でした。
具体的には、関数NewAppConfigで定義されているものです。

config.go(継承)
package inside

import "yamamon/flathand-bookmark/domain"

type AppConfig interface {
    GetAppConfig() domain.AppConfig
}


config.go(実装)
package inside

import (
    "os"
    "yamamon/flathand-bookmark/domain"
)

var (
    environment  string
)

const (
    LocalEnvironment = "local"
)

type AppConfig struct {
    Environment string
    ServiceName string
}

func NewAppConfig() *AppConfig {
    return &AppConfig{
        Environment: newEnvironment(),
        ServiceName: newServiceName(),
    }
}

func (a *AppConfig) GetAppConfig() domain.AppConfig {
    config := domain.AppConfig{
        Environment: a.Environment,
        ServiceName: a.ServiceName,
    }
    return config
}

func newEnvironment() string {
    environment = os.Getenv("ENVIRONMENT")
    if environment == "" {
        return LocalEnvironment
    }
    return environment
}

func newServiceName() string {
    return "flathand-bookmark"
}

付録

上記では、部分的なコードしか記載しなかったので、以下にディレクトリ構成とあわせてコードを記載しておきます。誰かの助けになれば幸いです。

ディレクトリ構成

├── domain
│   └── status.go
├── infrastructure
│   └── inside
│      └── config.go
├── interfaces
│   ├── controllers
│   │   ├── base.go
│   │   └── status.go
│   └── inside
│       ├── config.go
│       └── config_repository.go
├── main.go
├── routers
│   ├── commentsRouter_interfaces_controllers.go
│   └── router.go
└── usecase
    ├── config_interactor.go
    └── config_repository.go

コード

Entities層

domain/status.go
package domain

type AppConfig struct {
    Environment string `json:"environment"`
    ServiceName string `json:"serviceName" required:"true"`
}

Frameworks & Drivers層

infrastructure/inside/config.go
package inside

import (
    "os"
    "yamamon/flathand-bookmark/domain"
)

var (
    environment  string
)

const (
    LocalEnvironment = "local"
)

type AppConfig struct {
    Environment string
    ServiceName string
}

func NewAppConfig() *AppConfig {
    return &AppConfig{
        Environment: newEnvironment(),
        ServiceName: newServiceName(),
    }
}

func (a *AppConfig) GetAppConfig() domain.AppConfig {
    config := domain.AppConfig{
        Environment: a.Environment,
        ServiceName: a.ServiceName,
    }
    return config
}

func newEnvironment() string {
    environment = os.Getenv("ENVIRONMENT")
    if environment == "" {
        return LocalEnvironment
    }
    return environment
}

func newServiceName() string {
    return "flathand-bookmark"
}

routersに関しては、本来の役割からはframework&Device層ですが、infrastructureディレクトリとは分けています。

routers/router.go

import (
    "github.com/astaxie/beego"
    "yamamon/flathand-bookmark/infrastructure/inside"
    "yamamon/flathand-bookmark/interfaces/controllers"
)

func init() {
    ns := beego.NewNamespace("/v1",
        beego.NSNamespace("/statuses",
            beego.NSInclude(
                controllers.NewStatusController(inside.NewAppConfig()),
            ),
        ),
    )
    beego.AddNamespace(ns)
}

interface層

interfaces/controllers/base.go
package controllers

import (
    "fmt"
    "github.com/astaxie/beego"
)

type BaseController struct {
    beego.Controller
}

type Error struct {
    ErrorMessage string
}

func (controller *BaseController) ServeResponseJSON(response interface{}) {
    controller.Data["json"] = response
    beego.Info(fmt.Sprintf("response: %+v", response))
    controller.ServeJSON()
}

func (controller *BaseController) ServeErrorJSON(message string, statusCode int) {
    response := Error{ErrorMessage: message}
    controller.Data["json"] = response
    controller.Ctx.Output.SetStatus(statusCode)
    beego.Info(fmt.Sprintf("error: %+v, status: %+v", response, statusCode))
    controller.ServeJSON()
}
intefaces/controllers/status.go
package controllers

import (
    "yamamon/flathand-bookmark/interfaces/inside"
    "yamamon/flathand-bookmark/usecase"
)

var statusController *StatusController

type StatusController struct {
    BaseController
    Interactor usecase.ConfigInteractor
}

func NewStatusController(appConfigInterface inside.AppConfig) *StatusController {
    return &StatusController{
        Interactor:     usecase.ConfigInteractor{
            ConfigRepository: &inside.ConfigRepository{
                AppConfig: appConfigInterface,
            },
        },
    }
}

// @router /_me [get]
func (controller *StatusController) Get() {
    appConfig := controller.Interactor.ShowAppConfig()
    controller.ServeResponseJSON(appConfig)
}
interfaces/inside/config.go
package inside

import "yamamon/flathand-bookmark/domain"

type AppConfig interface {
    GetAppConfig() domain.AppConfig
}
interfaces/inside/config_repository.go
package inside

import (
    "yamamon/flathand-bookmark/domain"
)

type ConfigRepository struct {
    AppConfig
}

func (repository *ConfigRepository) ShowAppConfig() domain.AppConfig {
    return repository.AppConfig.GetAppConfig()
}

usecase層

usecase/config_interactor.go
package usecase

import (
    "yamamon/flathand-bookmark/domain"
)

type ConfigInteractor struct {
    ConfigRepository ConfigRepository
}

func(interactor *ConfigInteractor) ShowAppConfig() domain.AppConfig {
    return interactor.ConfigRepository.ShowAppConfig()
}
config_repository.go
package usecase

import "yamamon/flathand-bookmark/domain"

type ConfigRepository interface {
    ShowAppConfig() domain.AppConfig
}

その他

main.go
package main

import (
    _ "yamamon/flathand-bookmark/routers"
    "github.com/astaxie/beego"
)

func main() {
    if beego.BConfig.RunMode == "dev" {
        beego.BConfig.WebConfig.DirectoryIndex = true
        beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
    }
    beego.Run()
}

参考文献

さいごに

これが僕の初めての投稿になります。記載内容や文章などにも多々不備があると思います。コメントいただけますと、本記事はもちろんのこと、後学のためにもなりますので、よろしくお願い致します。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4