2023-04-17 02:00:00+00:00

In microservice development, coupling core business logic directly to external APIs (like shipping providers, payment processors, or specific databases) is a common architectural pitfall. When the external provider changes their SDK or API schema, your entire core service breaks. Hexagonal Architecture (Ports and Adapters) isolates your core business domain from these external changes.

In Go, this separation is achieved beautifully using interfaces (Ports) and concrete implementations (Adapters).


1. Defining Ports as Go Interfaces

Ports belong to the core application package. They define *what* the core needs, not how it is done. For instance, a notification port doesn't care if it's sent via SMS or Email:

package core

// NotificationPort defines the outbound boundary
type NotificationPort interface {
    SendSMS(recipient string, message string) error
    SendEmail(recipient string, subject string, body string) error
}

2. Implementing Adapters

Adapters are located in external packages. They implement the interface using third-party SDKs. If you switch from Twilio to Infobip, you only write a new adapter—the core logic remains untouched:

package adapters

import "github.com/twilio/twilio-go"

type TwilioAdapter struct {
    client *twilio.RestClient
}

func (a *TwilioAdapter) SendSMS(recipient string, message string) error {
    // Concrete Twilio SDK API integration
    return nil
}

This design makes unit testing incredibly simple, as you can mock the Ports without needing to initialize database connections or trigger external network requests.