Skip to content

Instantly share code, notes, and snippets.

@rluders
Last active March 28, 2026 18:29
Show Gist options
  • Select an option

  • Save rluders/ce61d4f92f446e51444aa3446b43d4be to your computer and use it in GitHub Desktop.

Select an option

Save rluders/ce61d4f92f446e51444aa3446b43d4be to your computer and use it in GitHub Desktop.
A comprehensive Go backend architecture guide for building scalable, maintainable modular monoliths using Clean Architecture, Hexagonal principles, and DDD-inspired design.

CLAUDE.md

Stack

  • Go 1.22+, net/http (http.ServeMux)
  • sqlc (SQL-first)
  • Modular monolith
  • Clean + Hexagonal + DDD-inspired
  • github.com/rluders/httpsuite for HTTP

Core Rules

  • Modular monolith → modules must stay independent
  • Prefer stdlib and explicit code (no framework magic)
  • Keep dependencies minimal and intentional
  • sqlc is the source of truth (never edit generated code)
  • Routing via http.ServeMux

Architecture Principles

  • Domain owns business logic
  • Services = use cases (no HTTP concerns)
  • Repository interfaces in domain, implementations in infra
  • HTTP is an adapter only
  • Modules must not know each other directly
  • Communication only via:
    • interfaces
    • event bus

Modules (Key Concept)

System is organized by module, not layers.

Each module encapsulates:

  • domain (aggregates, entities, errors)
  • services (use cases)
  • repository interfaces
  • repository implementations
  • HTTP handlers
  • route registration
  • module bootstrap

A module must be:

  • independently understandable
  • independently testable
  • cohesive and isolated

Never create shared dumping packages.


Module Structure

Preferred:

internal/
  <module>/
    domain/
      aggregate/
      entity/
      repository.go
      service.go
      errors.go
    application/        # optional
    infra/
      repository/
      eventbus/
    transport/
      http/
        handlers/
        requests/
        responses/
        routes.go
    module.go

Small modules:

internal/<module>/
  aggregate/
  entity/
  repository.go
  service.go
  handler_create.go
  routes.go
  module.go

Domain

  • Aggregates define consistency boundaries
  • Only aggregate roots have repositories
  • Entities contain behavior (not just data)
  • Avoid anemic models
  • Use value objects when useful

Services

  • Hold use cases + orchestration
  • Depend only on:
    • repository interfaces
    • event bus
    • external interfaces
  • No transport concerns

Repositories

  • Interface → domain
  • Implementation → infra
  • Use sqlc
  • Keep SQL explicit
  • Methods reflect business intent

Handlers

  • One handler per endpoint (New...)
  • Responsibilities:
    • parse
    • validate
    • extract context/auth
    • call service
    • encode response

Must NOT:

  • contain business logic
  • access DB directly

Naming:

  • create_user_handler.go
  • get_user_handler.go

Request / Response

  • Use github.com/rluders/httpsuite
  • Keep types near transport layer
  • Validate early
  • Do not leak domain/DB models

Routing

  • Each module owns routes

module.RegisterRoutes(mux)


Database / Transactions

  • SQL-first (sqlc)
  • Explicit queries
  • Transactions in services
  • Use WithTx
  • Add indexes when needed

Errors

  • Handle explicitly
  • Wrap internally
  • Return safe responses
  • Avoid panic (except startup)

Concurrency

  • Always propagate context
  • No unmanaged goroutines
  • Use only when justified

Config / Logging

  • Explicit config, fail fast
  • No secrets in code
  • Prefer structured logs

Security

  • Validate all external input
  • Use parameterized SQL
  • Do not trust client headers

Naming

Avoid:

  • utils.go, models.go, handlers.go

Prefer:

  • create_order_handler.go
  • order_repository.go

Testing

  • Table-driven tests
  • Unit: domain/services
  • Integration: DB
  • HTTP: httptest

Cover:

  • happy path
  • validation errors
  • edge cases
  • transactions

Performance

  • Optimize when needed
  • Watch DB round trips, allocations, indexes

Bootstrapping

main.go wires:

  • config, DB, infra, modules, router, server

Modules expose:

  • NewModule(...)
  • RegisterRoutes(...)

Review Heuristics

Reject if:

  • modules coupled
  • business logic in handlers
  • services bypassed
  • sqlc bypassed
  • transactions hidden
  • generic files introduced

Quality Gate

  • gofmt, go vet, tests, race detector
  • sqlc generate if needed
  • boundaries preserved
  • handlers thin
  • services own use cases
  • transactions correct
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment