Core Implementation Guide
Concrete Go patterns for trace-first logging, clean architecture, and error handling.
Core Implementation Guide (Go)
This guide translates WordLoop's overarching Engineering Principles into explicit, copy-pasteable Go code for the wordloop-core service.
1. Concrete Trace-First Development
We rely on OpenTelemetry for all observability. Every inbound request starts a trace, and every outbound request cascades it.
Initializing a Span
A new operation must start a span. If extracting from an HTTP Gin context, pass c.Request.Context().
Passing Context
Context is King. Do not store context in structs. Pass it as the first parameter to every single Domain, Service, and Provider function. If you drop the context, you sever the distributed trace.
2. Concrete Error Handling
We use Go's errors.Is capabilities combined with purely defined "Sentinel Errors" to prevent database or HTTP leakage into our Domain logic.
Defining Sentinels
Define business rule errors in internal/core/domain/errors.go:
Wrapping & Mapping Errors in Providers
An Adapter (Provider) catching a third-party or infrastructure error must wrap it into a Domain error before returning it to the Service.
3. Concrete Dependency Injection
We use interface injection (Ports) to satisfy dependencies.
The Port (Defined by the Core)
The interface belongs in internal/core/gateway//service and is strictly defined using Domain language.
The Wiring (Entrypoint)
Constructor injection is used to assemble the pieces at startup without relying on globals.
4. Idiomatic Go & Standards
We do not aim to rewrite foundational guidance on writing excellent Go code. Instead, we adhere to established industry baselines and strictly map them to our internal engineering principles.
We expect all Wordloop Core engineers to intimately understand:
- Effective Go for language fundamentals.
- Uber Go Style Guide for practical, enterprise-grade formatting, concurrency, and pattern consensus.
Below is concrete guidance on how overarching Go idioms manifest as system-enforced architectural invariants.
Accept Interfaces, Return Structs (Clean Architecture)
The Go Idiom: "Accept interfaces, return structs."
The Principle Connection: This idiom is the bedrock of Clean Architecture (Ports and Adapters). Gateways (Ports) define the interfaces. Services accept those interfaces. Providers return concrete struct representations.
Goroutines and Context Loss (Trace-First)
The Go Idiom: "Don't leave goroutines hanging, and always pass context."
The Principle Connection: We practice Trace-First Observability. Executing a background goroutine without passing context severs the OpenTelemetry trace, blinding our dashboards to system behavior.
When spawning an asynchronous background task, use context.WithoutCancel (introduced in Go 1.21) or extract/inject the trace so the background span remains a child of the request trace—even if the HTTP client disconnects early.
Immutability in the Domain (Domain Purity)
The Go Idiom: Receiver types (Pointer vs Value semantics).
The Principle Connection: Our Domain layer must remain pure and free from unpredictable side effects.
When creating methods on Domain entities that calculate or evaluate state rather than modifying it, enforce immutability by exclusively using value receivers. This ensures core business rules remain deterministic, trivially unit-testable, and free from accidental pointer mutation.