It is common to start with one large validation function that checks inventory, payment rules, fraud signals, and shipping data all at once. That grows into brittle code quickly: every new rule touches the same function, testing becomes tedious, and reordering behavior is risky.
That is especially awkward in Go, where small functions and explicit composition are usually easier to maintain.
Chain of Responsibility breaks the workflow into focused handlers. Each handler checks one concern and optionally forwards the request to the next handler. In Go, the chain is built with a small interface and struct composition, not inheritance.
This example runs an order through four validation steps and collects the result from each handler so the caller can inspect the full pipeline.
types.go base_handler.go inventory_check_handler.go payment_validation_handler.go fraud_detection_handler.go address_validation_handler.go validation_chain_service.go example.go
package main
type ValidationResult struct {
HandlerName string
Valid bool
Errors [] string
}
type Item struct {
Name string
Price float64
Qty int
}
type Address struct {
Street string
City string
Zip string
Country string
}
type OrderData struct {
Items [] Item
PaymentMethod string
TotalAmount float64
ShippingAddress Address
}
type OrderValidator interface {
SetNext ( OrderValidator ) OrderValidator
Validate ( order OrderData ) [] ValidationResult
Check ( order OrderData ) ValidationResult
} package main
type BaseHandler struct {
next OrderValidator
}
func ( h * BaseHandler ) SetNext ( handler OrderValidator ) OrderValidator {
h . next = handler
return handler
}
func ( h * BaseHandler ) validateNext ( order OrderData ) [] ValidationResult {
if h . next != nil {
return h . next . Validate ( order )
}
return nil
} package main
import "fmt"
type InventoryCheckHandler struct {
BaseHandler
}
func ( h * InventoryCheckHandler ) Validate ( order OrderData ) [] ValidationResult {
result := h . Check ( order )
return append ([] ValidationResult { result }, h . validateNext ( order )...)
}
func ( h * InventoryCheckHandler ) Check ( order OrderData ) ValidationResult {
errors := make ([] string , 0 )
for _ , item := range order . Items {
if item . Qty <= 0 {
errors = append ( errors , fmt . Sprintf ( " %s has invalid quantity %d " , item . Name , item . Qty ))
}
if item . Name == "out-of-stock-item" {
errors = append ( errors , fmt . Sprintf ( " %s is out of stock" , item . Name ))
}
}
return ValidationResult {
HandlerName : "InventoryCheckHandler" ,
Valid : len ( errors ) == 0 ,
Errors : errors ,
}
} package main
import "fmt"
type PaymentValidationHandler struct {
BaseHandler
}
func ( h * PaymentValidationHandler ) Validate ( order OrderData ) [] ValidationResult {
result := h . Check ( order )
return append ([] ValidationResult { result }, h . validateNext ( order )...)
}
func ( h * PaymentValidationHandler ) Check ( order OrderData ) ValidationResult {
errors := make ([] string , 0 )
validMethods := [] string { "credit_card" , "debit_card" , "paypal" , "bank_transfer" }
isValidMethod := false
for _ , method := range validMethods {
if order . PaymentMethod == method {
isValidMethod = true
break
}
}
if ! isValidMethod {
errors = append ( errors , fmt . Sprintf ( "invalid payment method: %s " , order . PaymentMethod ))
}
if order . TotalAmount <= 0 {
errors = append ( errors , "total amount must be positive" )
}
return ValidationResult {
HandlerName : "PaymentValidationHandler" ,
Valid : len ( errors ) == 0 ,
Errors : errors ,
}
} package main
import "strings"
type FraudDetectionHandler struct {
BaseHandler
}
func ( h * FraudDetectionHandler ) Validate ( order OrderData ) [] ValidationResult {
result := h . Check ( order )
return append ([] ValidationResult { result }, h . validateNext ( order )...)
}
func ( h * FraudDetectionHandler ) Check ( order OrderData ) ValidationResult {
errors := make ([] string , 0 )
if order . TotalAmount > 10000 {
errors = append ( errors , "high value orders require manual review" )
}
if strings . EqualFold ( order . ShippingAddress . Country , "banned_country" ) {
errors = append ( errors , "shipping destination is blocked" )
}
if len ( order . Items ) == 0 {
errors = append ( errors , "empty orders should be rejected before payment" )
}
return ValidationResult {
HandlerName : "FraudDetectionHandler" ,
Valid : len ( errors ) == 0 ,
Errors : errors ,
}
} package main
import (
"regexp"
"strings"
)
type AddressValidationHandler struct {
BaseHandler
}
func ( h * AddressValidationHandler ) Validate ( order OrderData ) [] ValidationResult {
result := h . Check ( order )
return append ([] ValidationResult { result }, h . validateNext ( order )...)
}
func ( h * AddressValidationHandler ) Check ( order OrderData ) ValidationResult {
errors := make ([] string , 0 )
addr := order . ShippingAddress
if strings . TrimSpace ( addr . Street ) == "" {
errors = append ( errors , "street is required" )
}
if strings . TrimSpace ( addr . City ) == "" {
errors = append ( errors , "city is required" )
}
if strings . TrimSpace ( addr . Zip ) == "" {
errors = append ( errors , "zip code is required" )
}
if strings . TrimSpace ( addr . Country ) == "" {
errors = append ( errors , "country is required" )
}
zipPattern := regexp . MustCompile ( `^\d{5}(-\d{4})?$` )
if addr . Zip != "" && ! zipPattern . MatchString ( addr . Zip ) {
errors = append ( errors , "zip code format is invalid" )
}
return ValidationResult {
HandlerName : "AddressValidationHandler" ,
Valid : len ( errors ) == 0 ,
Errors : errors ,
}
} package main
type ValidationChainService struct {
chain OrderValidator
}
func NewValidationChainService () * ValidationChainService {
inventoryHandler := & InventoryCheckHandler {}
paymentHandler := & PaymentValidationHandler {}
fraudHandler := & FraudDetectionHandler {}
addressHandler := & AddressValidationHandler {}
inventoryHandler . SetNext ( paymentHandler ). SetNext ( fraudHandler ). SetNext ( addressHandler )
return & ValidationChainService { chain : inventoryHandler }
}
func ( s * ValidationChainService ) ValidateOrder ( order OrderData ) [] ValidationResult {
return s . chain . Validate ( order )
} package main
import "fmt"
func main () {
service := NewValidationChainService ()
order := OrderData {
Items : [] Item {
{ Name : "Laptop" , Price : 1000 , Qty : 1 },
{ Name : "Mouse" , Price : 50 , Qty : 2 },
},
PaymentMethod : "credit_card" ,
TotalAmount : 1100 ,
ShippingAddress : Address {
Street : "123 Main St" ,
City : "Phnom Penh" ,
Zip : "12000" ,
Country : "Cambodia" ,
},
}
results := service . ValidateOrder ( order )
fmt . Println ( "=== Chain of Responsibility in Go ===" )
for _ , result := range results {
fmt . Printf ( " %s valid= %t \n " , result . HandlerName , result . Valid )
for _ , err := range result . Errors {
fmt . Printf ( " - %s \n " , err )
}
}
}