Skip to content

Commit

Permalink
remove unnecessary implementation, use decorator for circuit breaker
Browse files Browse the repository at this point in the history
  • Loading branch information
irahardianto committed Dec 12, 2017
1 parent ee6b041 commit 47e1246
Show file tree
Hide file tree
Showing 15 changed files with 65 additions and 137 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

./service-pattern-go
service-pattern-go
vendor/
67 changes: 46 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,11 @@ You see, instead of calling directly to PlayerService, PlayerController uses the
sqlconn := new(infrastructures.SqlConnection)
sqlconn.InitDB()

playerRepository := new(repositories.PlayerRepository)
playerRepository := &repositories.PlayerRepository{}
playerRepository.Db.Db = sqlconn.GetDB()

playerService := new(services.PlayerService)
playerService.PlayerRepository = playerRepository
playerService := &services.PlayerService{}
playerService.PlayerRepository = &repositories.PlayerRepositoryWithCircuitBreaker{playerRepository}

playerController := controllers.PlayerController{}
playerController.PlayerService = playerService
Expand Down Expand Up @@ -436,28 +436,53 @@ Essentially circuit breaker works just like electrical circuit breakers, nothing

In our case, we will be using hystrix-go, it is a go port from Netflix's hystrix library, how it works is essentially the same, even hystrix-go supports turbine along with its hystrix dashboard, but in my case, I rather use the datadog plugins, since we are using datadog to monitor our system.

func (repository *PlayerRepository) GetPlayerByName(name string) (models.PlayerModel, error) {
For the sake of SOLID principle implementation in our codebase, we will add hystrix-go to our PlayerRepository leveraging decorator pattern, this will maintain our base repository implementation, the one that calls database, clean from modification and we will create its extension which is named PlayerRepositoryWithCircuitBreaker. If you recall we inject our service with PlayerRepositoryWithCircuitBreaker rather than the original PlayerRepository.

output := make(chan models.PlayerModel, 1)
hystrix.ConfigureCommand("get_player_by_name", hystrix.CommandConfig{Timeout: 1000})
errors := hystrix.Go("get_player_by_name", func() error {
playerService.PlayerRepository = &repositories.PlayerRepositoryWithCircuitBreaker{playerRepository}

conn := repository.Db.GetDB()

player := models.PlayerModel{}
conn.First(&player, "Name = ?", name)
output <- player
return nil
}, nil)
Base PlayerRepository implementation

select {
case out := <-output:
return out, nil
case err := <-errors:
println(err)
return models.PlayerModel{}, err
}
}
type PlayerRepository struct {
Db infrastructures.SqlConnection
}

func (repository *PlayerRepository) GetPlayerByName(name string) (models.PlayerModel, error) {

conn := repository.Db.GetDB()

player := models.PlayerModel{}
conn.First(&player, "Name = ?", name)

return player, nil
}

PlayerRepository extension implementation

type PlayerRepositoryWithCircuitBreaker struct {
PlayerRepository interfaces.IPlayerRepository
}

func (repository *PlayerRepositoryWithCircuitBreaker) GetPlayerByName(name string) (models.PlayerModel, error) {

output := make(chan models.PlayerModel, 1)
hystrix.ConfigureCommand("get_player_by_name", hystrix.CommandConfig{Timeout: 1000})
errors := hystrix.Go("get_player_by_name", func() error {

player, _ := repository.PlayerRepository.GetPlayerByName(name)

output <- player
return nil
}, nil)

select {
case out := <-output:
return out, nil
case err := <-errors:
println(err)
return models.PlayerModel{}, err
}
}
As you see here, it is very easy to implement hystrix-go circuit breaker, you just need to wrap your db call inside hystrix if the timeout reached, the circuit breaker will be tripped and all calls to database will be halt, error will be returned instead for future call until db service is up and healthy.


Expand Down
6 changes: 0 additions & 6 deletions controllers/PlayerController.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,3 @@ func (controller *PlayerController) GetPlayerScore(res http.ResponseWriter, req

json.NewEncoder(res).Encode(response)
}

func (controller *PlayerController) GetPlayerMessage(res http.ResponseWriter, req *http.Request) {

data := controller.PlayerService.GetPlayerMessage()
json.NewEncoder(res).Encode(data)
}
35 changes: 0 additions & 35 deletions helpers/SafeAPICall.go

This file was deleted.

1 change: 0 additions & 1 deletion interfaces/IPlayerRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ import (

type IPlayerRepository interface {
GetPlayerByName(name string) (models.PlayerModel, error)
GetPlayerMessageFromAPI() models.MessageModel
}
5 changes: 0 additions & 5 deletions interfaces/IPlayerService.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package interfaces

import (
"github.com/irahardianto/service-pattern-go/models"
)

type IPlayerService interface {
GetScores(player1Name string, player2Name string) (string, error)
GetPlayerMessage() models.MessageModel
}
14 changes: 0 additions & 14 deletions interfaces/mocks/IPlayerRepository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 0 additions & 15 deletions interfaces/mocks/IPlayerService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import (
"net/http"
)

func init() {

}

func main() {

r := ChiRouter().InitRouter()
Expand Down
5 changes: 0 additions & 5 deletions models/MessageModel.go

This file was deleted.

31 changes: 14 additions & 17 deletions repositories/PlayerRepository.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
package repositories

import (
"encoding/json"

"github.com/afex/hystrix-go/hystrix"
"github.com/irahardianto/service-pattern-go/helpers"
"github.com/irahardianto/service-pattern-go/infrastructures"
"github.com/irahardianto/service-pattern-go/interfaces"
"github.com/irahardianto/service-pattern-go/models"

_ "github.com/jinzhu/gorm/dialects/sqlite"
)

type PlayerRepository struct {
Db infrastructures.SqlConnection
SafeAPICall helpers.SafeAPICall
type PlayerRepositoryWithCircuitBreaker struct {
PlayerRepository interfaces.IPlayerRepository
}

func (repository *PlayerRepository) GetPlayerByName(name string) (models.PlayerModel, error) {
func (repository *PlayerRepositoryWithCircuitBreaker) GetPlayerByName(name string) (models.PlayerModel, error) {

output := make(chan models.PlayerModel, 1)
hystrix.ConfigureCommand("get_player_by_name", hystrix.CommandConfig{Timeout: 1000})
errors := hystrix.Go("get_player_by_name", func() error {

conn := repository.Db.GetDB()
player, _ := repository.PlayerRepository.GetPlayerByName(name)

player := models.PlayerModel{}
conn.First(&player, "Name = ?", name)
output <- player
return nil
}, nil)
Expand All @@ -39,14 +34,16 @@ func (repository *PlayerRepository) GetPlayerByName(name string) (models.PlayerM
}
}

func (repository *PlayerRepository) GetPlayerMessageFromAPI() models.MessageModel {
type PlayerRepository struct {
Db infrastructures.SqlConnection
}

func (repository *PlayerRepository) GetPlayerByName(name string) (models.PlayerModel, error) {

//brokenEndpoint := "http://www.mocky.io/v2/599969590f0000880206f125"
workingEndpoint := "http://www.mocky.io/v2/599967ae0f0000880206f11e"
conn := repository.Db.GetDB()

callAPI := repository.SafeAPICall.Get("hello_world", workingEndpoint, 1000)
var data models.MessageModel
json.Unmarshal(callAPI, &data)
player := models.PlayerModel{}
conn.First(&player, "Name = ?", name)

return data
return player, nil
}
1 change: 0 additions & 1 deletion router.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ func (router *router) InitRouter() *chi.Mux {

r := chi.NewRouter()
r.HandleFunc("/getScore/{player1}/vs/{player2}", playerController.GetPlayerScore)
r.HandleFunc("/getMessage", playerController.GetPlayerMessage)

return r
}
Expand Down
Binary file removed service-pattern-go
Binary file not shown.
8 changes: 4 additions & 4 deletions servicecontainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ type kernel struct{}

func (k *kernel) InjectPlayerController() controllers.PlayerController {

sqlconn := new(infrastructures.SqlConnection)
sqlconn := &infrastructures.SqlConnection{}
sqlconn.InitDB()

playerRepository := new(repositories.PlayerRepository)
playerRepository := &repositories.PlayerRepository{}
playerRepository.Db.Db = sqlconn.GetDB()

playerService := new(services.PlayerService)
playerService.PlayerRepository = playerRepository
playerService := &services.PlayerService{}
playerService.PlayerRepository = &repositories.PlayerRepositoryWithCircuitBreaker{playerRepository}

playerController := controllers.PlayerController{}
playerController.PlayerService = playerService
Expand Down
8 changes: 0 additions & 8 deletions services/PlayerService.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package services

import (
"github.com/irahardianto/service-pattern-go/interfaces"
"github.com/irahardianto/service-pattern-go/models"
)

type PlayerService struct {
Expand Down Expand Up @@ -41,10 +40,3 @@ func (service *PlayerService) GetScores(player1Name string, player2Name string)

return result, nil
}

func (service *PlayerService) GetPlayerMessage() models.MessageModel {

data := service.PlayerRepository.GetPlayerMessageFromAPI()

return data
}

0 comments on commit 47e1246

Please sign in to comment.