Irbisa · Cheatsheet
Architecture & Patterns in Go
SOLID in Go, Clean and hexagonal architecture, DDD building blocks, CQRS, Go-idiomatic patterns, dependency injection
What do the five SOLID principles say, and what does each one look like in Go, a language without classes or inheritance?
What do DRY, KISS and YAGNI mean, and when does removing duplication make Go code worse?
What do low coupling and high cohesion mean, and how do a Go package's imports and exported names show that it has neither?
ReadOnlyCachesatisfiesCacheby returning an error fromSet. Which SOLID principle does it break, and how do you fix the design?type Cache interface { Get(ctx context.Context, key string) ([]byte, bool) Set(ctx context.Context, key string, val []byte) error} var ErrReadOnly = errors.New("cache is read-only") // Serves a snapshot embedded in the binary.type ReadOnlyCache struct{ data map[string][]byte } func (c *ReadOnlyCache) Get(_ context.Context, key string) ([]byte, bool) { v, ok := c.data[key] return v, ok} func (c *ReadOnlyCache) Set(context.Context, string, []byte) error { return ErrReadOnly }What is the difference between Dependency Inversion, Dependency Injection and Inversion of Control, and which of them does a hand-wired Go
mainuse?How do you do dependency injection in Go, and do you need a framework like
wireorfx?package main import ( "context" "database/sql" "log"…Which classic design patterns do you actually use in Go, and which ones does the language make unnecessary or reduce to a single function?
How do you implement the Strategy pattern in Go, and when is a function type like
http.HandlerFuncbetter than a one-method interface?type Order struct { TotalCents int64 Coupon string} // The strategy: how the discount is computed.…Find the bug: after wrapping
UserRepoin a caching decorator,UpdateEmailsucceeds butGetkeeps returning the old email until the TTL expires.type cachedRepo struct { UserRepo // methods not defined below go straight to the database cache *ttlCache[int64, User]} func (r *cachedRepo) Get(ctx context.Context, id int64) (User, error) { if u, ok := r.cache.Get(id); ok { return u, nil } u, err := r.UserRepo.Get(ctx, id) if err == nil { r.cache.Set(id, u) } return u, err}What are Clean Architecture and hexagonal (ports and adapters) architecture, what is the dependency rule, and when is it overkill for a Go service?
What makes a repository interface leak the database (
*sql.Rows, SQL fragments, ORM models in its signatures), and what should it expose instead?// wrong: the database leaks through the interface.type LeakyOrderRepo interface { Query(ctx context.Context, where string, args ...any) (*sql.Rows, error) Get(ctx context.Context, id int64) (OrderRow, error) // sql.NullString fields inside}…How do you keep a third-party SDK's types (a payment provider, an S3 client) out of your domain code, and what does an anti-corruption layer buy you?
// package payment: the port, in the domain's own terms.var ErrDeclined = errors.New("payment declined") type Charge struct { AmountCents int64 Currency string // ISO 4217: "EUR"…What are an entity, a value object and an aggregate in DDD, and how do you protect an aggregate's invariants in Go when struct fields are exported?
// Money is a value object: no identity, compared with ==, never mutated.type Money struct { Cents int64 Currency string}…Go has no sealed classes or enums with data. How do you model a closed set of variants, such as payment methods or order states, so a missing case is caught?
// Method is a closed set: isMethod is unexported, so no other package can add a variant.////sumtype:decltype Method interface{ isMethod() } type Card struct{ Last4 string }…What is a bounded context in DDD, and how does it decide where one Go package or service ends and the next one begins?
What is CQRS, when is a separate read model worth it, and what does it cost you in consistency?
What is event sourcing, how does it differ from storing current state plus an outbox, and what makes it hard to run in production?
type Event interface{ isEvent() } type Placed struct{ ID string }type LineAdded struct{ Cents int64 }type Paid struct{}…