Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go Pattern Design

Go Pattern Design

LINE Developers Thailand

September 06, 2022
Tweet

More Decks by LINE Developers Thailand

Other Decks in Technology

Transcript

  1. Go Pattern Design


    Mic Pantakan Ketbamrung

    View Slide

  2. • Hexagonal Architecture


    • Port and Adapters pattern


    • Example
    Agenda

    View Slide

  3. Dependency

    View Slide

  4. View Slide

  5. type cart struct{}



    func (c *cart) checkout() (string, error) {


    // do something before update inventory...


    body := strings.NewReader(`{


    "id": "line_sticker_001",


    "quantity": -1


    }`)


    res, _ := http.Post("https://line.me/myshop/v1/inventory",


    "application/json",


    body)


    return res.Status, nil


    }
    Checkout

    View Slide

  6. Matías Varela
    “Before writing the first line of code, one may ask:


    How should I organize the project? or, Which component should I write first?.


    If these questions are difficult to answer is because you are not following a
    software architecture pattern.”
    https://medium.com/@matiasvarela/hexagonal-architecture-in-go-cfd4e436faa3

    View Slide

  7. Port and Adapters

    ( A.K.A Hexagonal Architecture )



    The hexagonal architecture was defined by Alistair Cockburn in 2005

    View Slide

  8. Clean Architecture

    View Slide

  9. Port and Adapters

    View Slide

  10. Driver

    • Usb C


    Driven

    • 3.5 jack
    Port
    https://medium.com/@matiasvarela/hexagonal-architecture-in-go-cfd4e436faa3

    View Slide

  11. Adapters
    Driver


    • Usb C


    Driven


    • 3.5 jack
    https://medium.com/@matiasvarela/hexagonal-architecture-in-go-cfd4e436faa3

    View Slide

  12. Dependency Injection
    • Able to change easily
    https://medium.com/@matiasvarela/hexagonal-architecture-in-go-cfd4e436faa3

    View Slide

  13. GO

    View Slide

  14. type macBook struct{}


    func (*MacBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(laptop macBook) {


    laptop.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := macBook{}


    person.charge(myMacBook)


    }


    Comparing
    type usbC interface {


    chargeWithUsbC()


    }


    type macBook struct{}


    func (*macBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(u usbC) {


    u.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := &macBook{}


    person.charge(myMacBook)


    }

    View Slide

  15. type macBook struct{}


    func (*MacBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(laptop macBook) {


    laptop.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := macBook{}


    person.charge(myMacBook)


    }


    Comparing

    View Slide

  16. Comparing
    type usbC interface {


    chargeWithUsbC()


    }


    type macBook struct{}


    func (*macBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(u usbC) {


    u.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := &macBook{}


    person.charge(myMacBook)


    }

    View Slide

  17. type usbC interface {


    chargeWithUsbC()


    }


    type macBook struct{}


    func (*macBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(u usbC) {


    u.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := &macBook{}


    person.charge(myMacBook)


    }
    Port

    View Slide

  18. type usbC interface {


    chargeWithUsbC()


    }


    type macBook struct{}


    func (*macBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(u usbC) {


    u.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := &macBook{}


    person.charge(myMacBook)


    }
    Port

    View Slide

  19. type usbC interface {


    chargeWithUsbC()


    }


    type macBook struct{}


    func (*macBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(u usbC) {


    u.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := &macBook{}


    person.charge(myMacBook)


    }
    Port

    View Slide

  20. type usbC interface {


    chargeWithUsbC()


    }


    type macBook struct{}


    func (*macBook) chargeWithUsbC() {


    fmt.Println("MacBook is charging")


    }


    type person struct{}


    func (*person) charge(u usbC) {


    u.chargeWithUsbC()


    }


    func main() {


    person := &person{}


    myMacBook := &macBook{}


    person.charge(myMacBook)


    }
    Port

    View Slide

  21. type iPhone struct{}


    func (*iPhone) chargeWithLightingPort() {


    fmt.Println("iPhone is charging")


    }


    Port

    View Slide

  22. type iPhone struct{}


    func (*iPhone) chargeWithLightingPort() {


    fmt.Println("iPhone is charging")


    }


    type lightingToUsbC struct {


    iPhone *iPhone


    }


    func (*lightingToUsbC) chargeWithUsbC() {


    l.iPhone.chargeWithLightingPort()


    }


    Adapters

    View Slide

  23. type usbC interface {


    chargeWithUsbC()


    }



    type iPhone struct{}


    func (*iPhone) chargeWithLightingPort() {


    fmt.Println("iPhone is charging")


    }


    type lightingToUsbC struct {


    iPhone *iPhone


    }


    func (l *lightingToUsbC) chargeWithUsbC() {


    l.iPhone.chargeWithLightingPort()


    }


    func main() {


    person := &person{}


    myPhone := &lightingToUsbC{&iPhone{}}


    person.charge(myPhone)


    }


    Port & Adapter

    View Slide

  24. Example

    View Slide

  25. type cart struct{}



    func (c *cart) checkout() (string, error) {


    // do something before update inventory...


    body := strings.NewReader(`{


    "id": "line_sticker_001",


    "quantity": -1


    }`)


    res, _ := http.Post("https://line.me/myshop/v1/inventory",


    "application/json",


    body)


    return res.Status, nil


    }
    cart.go

    View Slide

  26. func Test_CheckoutSuccess(t *testing.T) {


    testCart := cart{}


    status, _ := testCart.checkout()


    want := "200 OK"


    if status != want {


    t.Errorf("status should be %s but got %s", want, status)


    }


    }


    cart_test.go

    View Slide

  27. func Test_CheckoutSuccess(t *testing.T) {


    testCart := cart{}


    status, _ := testCart.checkout()


    want := "200 OK"


    if status != want {


    t.Errorf("status should be %s but got %s", want, status)


    }


    }


    cart_test.go

    View Slide

  28. Implementation

    View Slide

  29. type httpClient interface {


    Post(url, contentType string, body io.Reader) (*http.Response, error)


    }


    type cart struct {


    httpClient


    }


    func (c *cart) checkout() string {


    // do something before update inventory...


    body := strings.NewReader(`{


    "id": "line_sticker_001",


    "quantity": -1


    }`)


    res, _ := c.Post("https://line.me/myshop/v1/inventory",


    "application/json",


    body)


    return res.Status


    }


    func main() {


    cli := http.DefaultClient


    cart := &cart{cli}


    cart.checkout()


    }
    cart.go

    View Slide

  30. type httpClient interface {


    Post(url, contentType string, body io.Reader) (*http.Response, error)


    }


    type cart struct {


    httpClient


    }


    func (c *cart) checkout() string {


    // do something before update inventory...


    body := strings.NewReader(`{


    "id": "line_sticker_001",


    "quantity": -1


    }`)


    res, _ := c.Post("https://line.me/myshop/v1/inventory",


    "application/json",


    body)


    return res.Status


    }


    func main() {


    cli := http.DefaultClient


    cart := &cart{cli}


    cart.checkout()


    }
    cart.go

    View Slide

  31. type httpClient interface {


    Post(url, contentType string, body io.Reader) (*http.Response, error)


    }


    type cart struct {


    httpClient


    }


    func (c *cart) checkout() string {


    // do something before update inventory...


    body := strings.NewReader(`{


    "id": "line_sticker_001",


    "quantity": -1


    }`)


    res, _ := c.Post("https://line.me/myshop/v1/inventory",


    "application/json",


    body)


    return res.Status


    }


    func main() {


    cli := http.DefaultClient


    cart := &cart{cli}


    cart.checkout()


    }
    cart.go

    View Slide

  32. type httpClient interface {


    Post(url, contentType string, body io.Reader) (*http.Response, error)


    }


    type cart struct {


    httpClient


    }


    func (c *cart) checkout() string {


    // do something before update inventory...


    body := strings.NewReader(`{


    "id": "line_sticker_001",


    "quantity": -1


    }`)


    res, _ := c.Post("https://line.me/myshop/v1/inventory",


    "application/json",


    body)


    return res.Status


    }


    func main() {


    cli := http.DefaultClient


    cart := &cart{cli}


    cart.checkout()


    }
    cart.go

    View Slide

  33. type httpClient interface {


    Post(url, contentType string, body io.Reader) (*http.Response, error)


    }


    type cart struct {


    httpClient


    }


    func (c *cart) checkout() string {


    // do something before update inventory...


    body := strings.NewReader(`{


    "id": "line_sticker_001",


    "quantity": -1


    }`)


    res, _ := c.Post("https://line.me/myshop/v1/inventory",


    "application/json",


    body)


    return res.Status


    }


    func main() {


    cli := http.DefaultClient


    cart := &cart{cli}


    cart.checkout()


    }
    cart.go

    View Slide

  34. type mockSuccessClient struct{}


    func (m *mockSuccessClient) Post(url, contentType string, body io.Reader) (*http.Response, error) {


    res := &http.Response{


    Status: "200 OK",


    }


    return res, nil


    }


    func Test_CheckoutSuccess(t *testing.T) {


    myClient := &mockSuccessClient{}


    cart := &cart{myClient}


    status := cart.checkout()


    want := "200 OK"


    if status != want {


    t.Errorf("status should be %s but got %s", want, status)


    }


    }


    cart_test.go

    View Slide

  35. type mockSuccessClient struct{}


    func (m *mockSuccessClient) Post(url, contentType string, body io.Reader) (*http.Response, error) {


    res := &http.Response{


    Status: "200 OK",


    }


    return res, nil


    }


    func Test_CheckoutSuccess(t *testing.T) {


    myClient := &mockSuccessClient{}


    cart := &cart{myClient}


    status := cart.checkout()


    want := "200 OK"


    if status != want {


    t.Errorf("status should be %s but got %s", want, status)


    }


    }


    cart_test.go

    View Slide

  36. Change HTTP Client

    View Slide

  37. Resty docs
    https://github.com/go-resty/resty

    View Slide

  38. Resty docs
    https://github.com/go-resty/resty

    View Slide

  39. type myResty struct {


    *resty.Client


    }


    func NewRestyClient() httpClient {


    return myResty{}


    }


    func (client myResty) Post(url, contentType string, body io.Reader) (*http.Response, error) {


    resp, _ := client.R().


    SetBody(body).


    ForceContentType(contentType).


    Post(“https://line.me/myshop/v1/inventory")


    return resp.RawResponse, nil


    }


    .


    .


    .


    func main() {


    newCli := NewRestyClient()


    cart := &cart{newCli}


    cart.checkout()


    }
    my_restry.go

    View Slide