# Notes: Go Design Patterns - Mediator Pattern

Mediator pattern is used to reduce communication complexity between multiple objects or classes. This pattern provides a *mediator class that typically handles all the communications between different classes* and supports the easy maintainability of the code by loose coupling. This is a behavioral pattern.

In our example, we are going to use `ServiceCaller` object to decouple communication between different services. `ServiceCaller` contains the default go HTTP client. We can replace it with whatever client we want without affecting the code on the objects' communication side.

```
type ServiceCaller struct {
	httpClient *http.Client
}
``` 

Here we have two external services `USER_DETAIL_SERVICE` and `USER_POST_SERVICE`. And to handle these services, we have created two handlers in our current service `UserServiceHandler` and `PostServiceHandler`. These handlers wrap `ServiceCaller` to be used as a **mediator**.

```
type UserServiceHandler struct {
	sc ServiceCaller
}
```
```
type PostServiceHandler struct {
	sc ServiceCaller
}
```

In `UserServiceHandler` we have `GetAllUsers` method which calls `USER_DETAIL_SERVICE` to get the data of all the users.
```
func (h *UserServiceHandler) GetAllUsers() {
	resp, err := h.sc.Get(USER_DETAIL_SERVICE + "/users")

	if err != nil {
		fmt.Println(err.Error())
	} else {
		fmt.Printf("%#v\n\n", string(resp))
	}
}
```

Likewise, `PostServiceHandler` has `SavePost` which saves posts by calling `USER_POST_SERVICE`.
```
func (h *PostServiceHandler) SavePost(payload string) {
	resp, err := h.sc.Post(USER_POST_SERVICE+"/posts", payload)

	if err != nil {
		fmt.Println(err.Error())
	} else {
		fmt.Printf("%#v\n\n", string(resp))
	}
}
```

Now to communicate with the external services, we use `ServiceCaller` object as the **mediator**. `ServiceCaller` takes care of all the logic required to make an external call (e.g. HTTP methods) and returns the service response in the required format. In our implementation, we have created `Get` and `Post` methods for *GET* and *POST* method calls respectively.

Complete working code where you can see the complete result:

```
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
)

// ServiceCaller is a mediator
type ServiceCaller struct {
	httpClient *http.Client
}

func NewServiceCaller() *ServiceCaller {
	return &ServiceCaller{
		httpClient: http.DefaultClient,
	}
}

func (sc *ServiceCaller) Get(url string) ([]byte, error) {
	resp, err := sc.httpClient.Get(url)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return body, nil
}

func (sc *ServiceCaller) Post(url, JSONPayload string) ([]byte, error) {
	postData := bytes.NewBuffer([]byte(JSONPayload))
	resp, err := sc.httpClient.Post(url, "application/json", postData)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	return body, nil
}

// ServiceCallers to call different services wrapping our mediator
// service URIs
const (
	USER_DETAIL_SERVICE = "https://jsonplaceholder.typicode.com"
	USER_POST_SERVICE   = "https://jsonplaceholder.typicode.com"
)

type UserServiceHandler struct {
	sc ServiceCaller
}

func (h *UserServiceHandler) GetAllUsers() {
	resp, err := h.sc.Get(USER_DETAIL_SERVICE + "/users")

	if err != nil {
		fmt.Println(err.Error())
	} else {
		fmt.Printf("%#v\n\n", string(resp))
	}
}

type PostServiceHandler struct {
	sc ServiceCaller
}

func (h *PostServiceHandler) SavePost(payload string) {
	resp, err := h.sc.Post(USER_POST_SERVICE+"/posts", payload)

	if err != nil {
		fmt.Println(err.Error())
	} else {
		fmt.Printf("%#v\n\n", string(resp))
	}
}

func main() {
	serviceCaller := NewServiceCaller()

	// can use constructor instead
	userServiceHandler := UserServiceHandler{
		sc: *serviceCaller,
	}

	postServiceHandler := PostServiceHandler{
		sc: *serviceCaller,
	}

	payload := `{"title": "btree.dev", "body": "mediator pattern", "userId": 1}`
	postServiceHandler.SavePost(payload)

	userServiceHandler.GetAllUsers()
}

``` 

