A repository gives application logic a place to store and retrieve domain objects without putting storage code in an API handler. For a small game service, an in-memory repository is quick to build, but games disappear when the instance stops. With multiple instances, a request may reach one that does not hold the game. section 8.3 section 8.7
Keep storage in an internal/repository package and use domain types as the shared language between storage and handlers. An in-memory implementation can store those types directly. If a database needs storage-specific fields, use a separate storage structure and adapt it to the domain so the schema can evolve independently. section 8.3.1 section 8.4
A compact Go implementation starts with:
- A repository value containing a map keyed by game ID, plus New to initialize the map. section 8.4.2
- Separate Add, Find, and Update operations. Add rejects an existing ID; Find reports a missing ID; Update must not silently insert one. section 8.4.2
- One repository initialized at service startup and passed to the router. Give each handler only the methods it needs through a small interface, such as Add for game creation. section 8.4.3
Listing 8.30 newgame/handler.go: Minimal interface
type gameAdder interface { #1
Add(game session.Game) error
}
// Handler returns the handler for the game creation endpoint.
func Handler(db gameAdder) http.HandlerFunc { #2
return func(w http.ResponseWriter, _ *http.Request) {
// ...
}
}
The game-creation handler accepts a one-method interface rather than requiring the concrete repository; a test stub can implement that same interface. section 8.4.3
Test repository operations and handler behavior separately. Because HTTP requests run concurrently, protect a shared map from concurrent access with a mutex. For list operations, sort the results before returning them: map-backed storage alone does not give clients or tests a dependable order. section 8.4.2 section 8.4.3 section 10.5.1
A second use: saved habits
A habit-tracking service can start with another internal, in-memory repository. Its domain Create function accepts a small storage interface and wraps a save error with context. Repository methods take context.Context in anticipation of database calls that may need to stop when a client interrupts them. This is a practical first version, not a scalable persistence solution. section 10.3.3








