Skip to content

Instantly share code, notes, and snippets.

@pieterlouw
Created January 23, 2017 20:03
Show Gist options
  • Save pieterlouw/5bb6c99682504f2988d3e9ca1a4dd353 to your computer and use it in GitHub Desktop.
Save pieterlouw/5bb6c99682504f2988d3e9ca1a4dd353 to your computer and use it in GitHub Desktop.

Example 3: 3rd party library/API

In this example we call out to a 3rd party library/API to handle payments.

Let's see the request and response types:

type TransactionRequest struct {
	//request fields
}

type TransactionResponse struct {
	//request fields
}

type QueryRequest struct {
	//request fields
}

type QueryResponse struct {
	//request fields
}

The api type that will do the calls and use the above types will look something like this:

type httpRESTApi struct {
	url string,
	username string,
	password string,
	//other fields
}

func (a httpRESTApi) DoPayment(ctx *context.Context, req *TransactionRequest) (*TransactionResponse, error) {
	//marshal request to JSON

	//POST JSON payload to API endpoint

	//unmarshal JSON response payload  
}

func (a httpRESTApi) DoQuery(ctx *context.Context, req *QueryRequest) (*QueryResponse, error) {
	//marshal request to JSON

	//POST JSON payload to API endpoint

	//unmarshal JSON response payload  
}

Like the previous example, let's create a WebHandler type that will embed the concrete api type:

type WebHandler struct {
    *httpRESTApi
}

By looking at the behavior of the API we can see it can do a payment or query the outcome of a previous payment.

Using 3rd party libraries and API's are very common these days, but we've also seen that they can close down or not perform as we expect and experience lots of downtime. Or the 3rd library might support a new transport or message type (like gRPC and Protobufs) instead of JSON REST. By designing according to behavior using interfaces you can decouple yourself from change by using the interface type and change the underlying type at minimum cost.

Let's see how it's done.

The API interface type will define the common library/API behavior:

type PaymentService interface {
	DoPayment(ctx *context.Context, req *transactionRequest) (*transactionResponse, error)
	DoQuery(ctx *context.Context, req *queryRequest) (*queryResponse, error)
}

Again, by embedding the interface type instead of the concrete type in the WebHandler we can switch to a different concrete type with the same behavior without having to change WebHandler:

type WebHandler struct {
    PaymentService
}

Like in the previous example you can embed a PaymentRepository in the WebHandler struct type.

type WebHandler struct {
    PaymentService
    PaymentRepository
}

But this still bind us to only use this system as a web application.

If we make a PaymentHandler interface type we can open up the possibility to handle payments in more ways than just a web interface:

type PaymentHandler interface {
    PaymentService
    PaymentRepository
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment