package order
import (
"context"
"fmt"
)
// Small interfaces for each subsystem the facade hides.
type Inventory interface {1
Reserve(ctx context.Context, sku string, qty int) error
Release(ctx context.Context, sku string, qty int) error
}
type Payment interface {
Charge(ctx context.Context, customerID string, amount int64) (string, error)
}
type Shipping interface {
CreateShipment(ctx context.Context, address, sku string) (string, error)
}
type Checkout struct {
inv Inventory
pay Payment
ship Shipping
}
func NewCheckout(inv Inventory, pay Payment, ship Shipping) *Checkout {2
return &Checkout{inv: inv, pay: pay, ship: ship}
}
// PlaceOrder is the single entry point; callers never touch subsystems.
func (c *Checkout) PlaceOrder(ctx context.Context, req Request) (Receipt, error) {3
if err := c.inv.Reserve(ctx, req.SKU, req.Qty); err != nil {
return Receipt{}, fmt.Errorf("reserve stock: %w", err)
}
txID, err := c.pay.Charge(ctx, req.CustomerID, req.Total)
if err != nil {
_ = c.inv.Release(ctx, req.SKU, req.Qty) // compensate step 14
return Receipt{}, fmt.Errorf("charge: %w", err)
}
tracking, err := c.ship.CreateShipment(ctx, req.Address, req.SKU)
if err != nil {
return Receipt{}, fmt.Errorf("ship: %w", err)
}
return Receipt{TxID: txID, TrackingNo: tracking}, nil
}