Handlers should translate HTTP into application operations rather than own a global map. A small Repository interface makes that boundary visible: Create, List, Get and Delete.
The supplied MemoryStore is a complete bounded local implementation. It keeps at most 100 active notes, assigns monotonic IDs and synchronizes access. Those choices are sufficient for this learning API, but they do not provide durable storage, multi-process consistency or authorization.
In Create, the check and mutation must be one critical section. If two goroutines check capacity before either inserts, separate locks can permit an extra record. Likewise, reading and incrementing the ID counter separately can reuse an identity.
Duplicate titles are allowed. A title is editable data, not the record key. IDs stay monotonic after deletion so a later note does not silently reuse an earlier path.
package main
import ("fmt")
func main(){
notes:=map[int]string{1:"same",2:"same"};fmt.Println(len(notes));delete(notes,1);fmt.Println(notes[2])
}List returns a new non-nil slice sorted by ID. A non-nil empty slice encodes as [] rather than null, which keeps the API’s empty-collection response stable. Sorting avoids exposing Go map iteration order as an accidental public contract.
A returned container must not alias mutable internal storage. Here Note contains an int and immutable string value, so copying the Note is enough. If a future record contains slices, maps or pointers, decide whether deeper copying or another ownership strategy is needed.
The interface also supports failure testing. A fake repository can return an internal error so handler tests verify a stable 500 response without leaking private backend details. List’s interface deliberately has no error return in this tiny lab; a real storage adapter would need to revisit that limitation.
package main
import ("fmt";"encoding/json")
func main(){
empty:=make([]int,0);var missing []int;a,_:=json.Marshal(empty);b,_:=json.Marshal(missing);fmt.Println(string(a),string(b))
}Complete Create, then test full capacity, deletion followed by creation, repeated titles and concurrent calls. Inspect the supplied snapshot/get/delete contracts as part of the boundary.
Next we write a small read-only contract probe and show why status-only tests miss broken behavior.