Go
Database layer
Write the data access layer with database/sql.
In-memory repository
Writing an in-memory repository first, instead of a database, lets you try the service right away. Because the HTTP server handles requests concurrently, the data is protected with a sync.Mutex:
type memRepo struct {
mu sync.Mutex
urunler []Urun
}
func (r *memRepo) Ekle(u Urun) error {
r.mu.Lock()
defer r.mu.Unlock()
r.urunler = append(r.urunler, u)
return nil
}
Moving to a database
When you write a second type implementing the same Repo interface with database/sql, the service code doesn't change:
type sqlRepo struct{ db *sql.DB }
func (r sqlRepo) Ekle(u Urun) error {
_, err := r.db.Exec("INSERT INTO urunler(ad, fiyat) VALUES(?, ?)", u.Ad, u.Fiyat)
return err
}
main.go chooses which implementation to use; this is called dependency injection.
Exercise Memory repository
Write Urun and memRepo (sync.Mutex + []Urun). The Ekle and Liste methods work under the lock. Add two products and print len(r.Liste()).
Show example solution
package main
import (
"fmt"
"sync"
)
type Urun struct {
Ad string
Fiyat float64
}
type memRepo struct {
mu sync.Mutex
urunler []Urun
}
func (r *memRepo) Ekle(u Urun) error {
r.mu.Lock()
defer r.mu.Unlock()
r.urunler = append(r.urunler, u)
return nil
}
func (r *memRepo) Liste() []Urun {
r.mu.Lock()
defer r.mu.Unlock()
return append([]Urun(nil), r.urunler...)
}
func main() {
r := &memRepo{}
r.Ekle(Urun{"Kalem", 10})
r.Ekle(Urun{"Defter", 25})
fmt.Println(len(r.Liste()))
}
Test yourself
-
Why do memRepo methods use a pointer receiver (<code>*memRepo</code>)?
- For JSON
- So the mutex and slice aren't copied and changes stay on the same object
- Because it's shorter
- Because interfaces don't support value receivers
Show answer
So the mutex and slice aren't copied and changes stay on the same object — A value receiver copies on each call; the added product stays in the copy and a copied mutex protects nothing.
-
Release the lock
r.mu.Lock()
____ r.mu.Unlock()
return append([]Urun(nil), r.urunler...)
Show answer
r.mu.Lock()
defer r.mu.Unlock()
return append([]Urun(nil), r.urunler...)
-
Repository implementations
- memRepo
- In memory, for tests
- sqlRepo
- With database/sql
- Repo
- Common interface
- sync.Mutex
- Guards concurrent access
Next lesson: HTTP API