Module 1 · Exercise 2 · ~40 min · Real Go code

Method Sets, Interfaces, and Embedding

Build a tiny submitter pipeline that mirrors the shape of real Canton-adjacent middleware: an inner submitter with retries and prefixing wrapped around it. Demonstrates pointer receivers, the decorator pattern, the nil-interface trap in practice, and method promotion via embedding.

Where
cd module-01-go-mental-model/exercises/exercise-02-method-sets
go test -v ./...

What you're building

Three layers, composable in any order:

type Submitter interface { Submit(cmd Command) error }

// Decorator chain example:
inner := &RecordingSubmitter{}
withPrefix := &PrefixingSubmitter{Inner: inner, Prefix: "DEV:"}
withRetries := &RetryingSubmitter{Inner: withPrefix, MaxAttempts: 3}

withRetries.Submit(Command{ID: "x", Payload: "hello"})
// → tries up to 3 times → prefixes payload → records to inner

This is exactly the shape of a real Ledger API client middleware stack.

Three parts

  1. Build the pipeline pieces. RecordingSubmitter (writes to memory), PrefixingSubmitter (decorates payloads), RetryingSubmitter (retries on error).
  2. Handle nil interfaces. NewOkSubmitter must return a non-nil interface; WrapNilSafe must accept a nil inner without panic.
  3. Embedding. AuditedClient embeds *RecordingSubmitter, gets Submit for free, adds Audit() listing recorded command IDs.

Tips

Stretch (no test for this — yours to do for the practice)

Add a LoggingSubmitter that wraps any Submitter and prints "submitting cmd %s" to stdout before forwarding. Then wire up a four-layer chain:

recording → prefix → retry → log

Submit one command. Verify the log appears, the retry layer doesn't retry on success, the prefix is applied, and recording captures it.