Factory Method in Go: Creational Pattern with Clean/Hexagonal Implementation

2/4/2026

Design Pattern
Golang
Creational
Clean Architecture
Hexagonal
Factory Method in Go: Creational Pattern with Clean/Hexagonal Implementation thumbnail

Overview

Factory Method is a creational design pattern used to centralize object creation and avoid constructor leakage in service logic. In real backend services, this pattern helps teams keep modules decoupled while still shipping features quickly.

Problem It Solves

Without Factory Method, teams often mix orchestration, external integration, and domain rules in one place. That leads to high coupling, difficult testing, and expensive refactoring.

5W + 1H

What

Factory Method is a pattern that structures collaboration between components to solve a recurring design problem.

Why

To increase maintainability, isolate change, and enforce clear boundaries between policy and implementation detail.

Who

Backend engineers, platform engineers, and API teams working with integrations and evolving business logic.

When

Use Factory Method when your service complexity is growing and one responsibility starts bleeding into unrelated modules.

Where

Apply it in service boundaries such as application use cases, integration adapters, workflow orchestration, and domain policies.

How

Define stable ports first, then implement adapters and wire them through use-case orchestration.

Go Implementation (Step-by-step)

1) Port / interface definitions

go
package ports

import "context"

type FactoryMethodInput struct {
	RequestID string
	Payload   map[string]any
}

type FactoryMethodOutput struct {
	Status string
	Data   map[string]any
}

type FactoryMethodPort interface {
	Execute(ctx context.Context, in FactoryMethodInput) (FactoryMethodOutput, error)
}

2) Adapter / implementation

go
package adapters

import (
	"context"
	"fmt"
	"myapp/internal/ports"
)

type FactoryMethodAdapter struct {
	providerName string
}

func NewFactoryMethodAdapter(providerName string) *FactoryMethodAdapter {
	return &FactoryMethodAdapter{providerName: providerName}
}

func (a *FactoryMethodAdapter) Execute(ctx context.Context, in ports.FactoryMethodInput) (ports.FactoryMethodOutput, error) {
	if in.RequestID == "" {
		return ports.FactoryMethodOutput{}, fmt.Errorf("request id is required")
	}

	return ports.FactoryMethodOutput{
		Status: "ok",
		Data: map[string]any{
			"pattern": "Factory Method",
			"provider": a.providerName,
		},
	}, nil
}

3) Use-case / main wiring

go
package main

import (
	"context"
	"fmt"
	"log"
	"myapp/internal/adapters"
	"myapp/internal/ports"
)

func run(ctx context.Context, svc ports.FactoryMethodPort) error {
	out, err := svc.Execute(ctx, ports.FactoryMethodInput{
		RequestID: "REQ-1001",
		Payload: map[string]any{"source": "api"},
	})
	if err != nil {
		return err
	}

	fmt.Println("status:", out.Status, "pattern:", out.Data["pattern"])
	return nil
}

func main() {
	ctx := context.Background()
	service := adapters.NewFactoryMethodAdapter("default-provider")
	if err := run(ctx, service); err != nil {
		log.Fatal(err)
	}
}

Suggested Project Structure

text
cmd/
  api/
    main.go
internal/
  domain/
    factory-method-go/
      model.go
      policy.go
  application/
    factory-method-go/
      usecase.go
  ports/
    factory-method-go_port.go
  adapters/
    inbound/
      http/
        factory-method-go_handler.go
    outbound/
      factory-method-go_adapter.go
  infrastructure/
    config/
      loader.go
    persistence/
      repository.go

Clean/Hexagonal Placement

Boundary Rules

  1. Dependencies point inward: adapters/infrastructure depend on ports/domain, not the reverse.
  2. Domain must stay framework-agnostic (no HTTP, DB driver, or vendor SDK import).
  3. Application layer should know interfaces, not concrete adapters.
  4. Infrastructure may know everything technical, but should not hold business policy.

Real-World Use Case

In production, Factory Method is useful to select notifier implementation by config (email, slack, webhook). You can keep your use case stable while replacing providers or transport implementations with minimal changes.

Benefits & Tradeoffs

Benefits

Tradeoffs

Common Pitfalls

  1. Putting domain rules in adapters.
  2. Leaking vendor-specific payload directly into domain models.
  3. Creating too many abstractions without concrete change pressure.
  4. Skipping contract tests for port behavior.

When NOT to use

Conclusion

Factory Method is most effective when paired with Clean/Hexagonal boundaries. The pattern gives structure, while architecture keeps dependencies under control as the codebase evolves.