Design patterns in Go: Adapter

01 Jan 2021

There might be cases when two different types of objects should be used for a similar processing. It’s easy when those types share similar interface, but how about the cases when interfaces are different. Obviously, there should be a mechanism for translating the difference to the consumer object. Here is where Adapter design patern comes to help.


Let’s imagine a case, that you have integrated a payment service, and you have only single payment functionality in place. But, here comes a request form bussiness, in which a subscription should be added, eventually a subscription payment should be added as well. Let’s consider the third party payment service pretty rudimentary, and it does not have it’s own subscription payments functionality, only transactions API.

This way you need to adapt the incoming payment request, in order to perform a transaction. For that purpose an adapter structure will be created in order to translate the subscription payment request.


Let’s take a look on the code:

type StripeService struct{}

func NewStripeService() StripeService {
	return StripeService{}

func (s StripeService) Execute(amount int, sender, receiver string) error {
	var err error

	// ... handling all Stripe API transaction logic, and return err if it occurs
	fmt.Printf("Paying $%d from %s to %s\n", amount, sender, receiver)

	return err

type Service interface {
	Execute(amount int, sender, receiver string) error

type PaymentsService struct {
	service Service

func NewPaymentsService(service Service) PaymentsService {
	return PaymentsService{service}

func (p PaymentsService) MakeTransaction(payment Payment) error {
	var err error

	err = p.service.Execute(

	return err

Here are defined the Stripe library with specific interface, and payments service, which actually handles the payment logic.

Next, we will take a look on SinglePayment structure:

type Payment interface {
	GetAmount() int
	GetRecipientID() string
	GetSenderID() string

type SinglePayment struct {
	senderID    string
	recipientID string
	amount      int

func NewSinglePayment(amount int, senderID, recipientID string) *SinglePayment {
	return &SinglePayment{senderID, recipientID, amount}

func (sp SinglePayment) GetAmount() int {
	return sp.amount

func (sp SinglePayment) GetSenderID() string {
	return sp.senderID

func (sp SinglePayment) GetRecipientID() string {
	return sp.recipientID

Now, let’s assume that subscription structure will have a different interface, like following:

type SubscriptionPayment struct {
	customerID       string
	subscriptionPlan string
	units            int

func NewSubscriptionPayment(customerId, subscriptionPlan string, units int) *SubscriptionPayment {
	return &SubscriptionPayment{customerId, subscriptionPlan, units}

func (sb SubscriptionPayment) GetCustomerPaymentID() (string, error) {
	customer, err := customers.Find(sb.customerID)
	if err != nil {
		return "", err

	return customer.paymentId, nil

func (sb SubscriptionPayment) GetSubscriptionPlan() string {
	return sb.subscriptionPlan

func (sb SubscriptionPayment) GetTimeUnits() int {
	return sb.units

In order to make it suitable for PaymentsService structure, we need to implement Payment interface. However that would break Single Responsibility Principle of this struct. This means we will need an adapter structure defined:

type SubscriptionPaymentsAdapter struct {
	payment           SubscriptionPayment
	subscriptionPlans map[string]int

func NewSubscriptionPaymentsAdapter(payment SubscriptionPayment) *SubscriptionPaymentsAdapter {
	plans := map[string]int{
		"MONTHLY": 9990,
		"YEARLY":  24990,
	return &SubscriptionPaymentsAdapter{payment, plans}

func (sp SubscriptionPaymentsAdapter) GetAmount() int {
	planFee, ok := sp.subscriptionPlans[sp.payment.GetSubscriptionPlan()]
	if !ok {
		return 0
	return planFee * sp.payment.GetTimeUnits()

func (sp SubscriptionPaymentsAdapter) GetSenderID() string {
	id, _ := sp.payment.GetCustomerPaymentID()
	return id

func (sp SubscriptionPaymentsAdapter) GetRecipientID() string {
	return os.Getenv("COMPANY_PAYMENT_ID")

The code for handling this kind of architecture will be as follows:

	var payment Payment
	service := NewPaymentsService(NewStripeService())
	payment = NewSinglePayment(200, "A", "B")


	subscriptionPayment := NewSubscriptionPayment("2", "MONTHLY", 2)
	payment = NewSubscriptionPaymentsAdapter(*subscriptionPayment)

This way, the subscription payment, that at first sight is not compatible with payment interface, can be used as a payment. Of course this example illustrates only the payment logic part, but this can be valable for different functionalities as well.


This pattern is also useful for wrapping up some third party library code so that can not be changed, and it will provide application specific logic, based on other kind of interfaces