Introduced interfaces for all repositories, completely isolating the persistence layer from the repositories usage and specification

This commit is contained in:
Deluan 2016-03-02 09:33:49 -05:00
parent 272a499c7e
commit 300ed0d9a4
15 changed files with 83 additions and 48 deletions

View file

@ -8,4 +8,10 @@ type Album struct {
Year int Year int
Compilation bool Compilation bool
Rating int Rating int
}
type AlbumRepository interface {
BaseRepository
Put(m *Album) error
Get(id string) (*Album, error)
} }

View file

@ -3,4 +3,12 @@ package domain
type Artist struct { type Artist struct {
Id string Id string
Name string Name string
} }
type ArtistRepository interface {
BaseRepository
Put(m *Artist) error
Get(id string) (*Artist, error)
GetByName(name string) (*Artist, error)
}

8
domain/base.go Normal file
View file

@ -0,0 +1,8 @@
package domain
type BaseRepository interface {
NewId(fields ...string) string
CountAll() (int, error)
}

View file

@ -12,6 +12,7 @@ type ArtistIndex struct {
type ArtistIndexRepository interface { type ArtistIndexRepository interface {
BaseRepository
Put(m *ArtistIndex) error Put(m *ArtistIndex) error
Get(id string) (*ArtistIndex, error) Get(id string) (*ArtistIndex, error)
GetAll() ([]ArtistIndex, error) GetAll() ([]ArtistIndex, error)

View file

@ -15,4 +15,9 @@ type MediaFile struct {
Compilation bool Compilation bool
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
}
type MediaFileRepository interface {
BaseRepository
Put(m *MediaFile) error
} }

View file

@ -4,4 +4,9 @@ type MediaFolder struct {
Id string Id string
Name string Name string
Path string Path string
} }
type MediaFolderRepository interface {
GetAll() ([]MediaFolder, error)
}

View file

@ -4,24 +4,24 @@ import (
"github.com/deluan/gosonic/domain" "github.com/deluan/gosonic/domain"
) )
type Album struct { type albumRepository struct {
BaseRepository baseRepository
} }
func NewAlbumRepository() *Album { func NewAlbumRepository() domain.AlbumRepository {
r := &Album{} r := &albumRepository{}
r.init("album", &domain.Album{}) r.init("album", &domain.Album{})
return r return r
} }
func (r *Album) Put(m *domain.Album) error { func (r *albumRepository) Put(m *domain.Album) error {
if m.Id == "" { if m.Id == "" {
m.Id = r.NewId(m.ArtistId, m.Name) m.Id = r.NewId(m.ArtistId, m.Name)
} }
return r.saveOrUpdate(m.Id, m) return r.saveOrUpdate(m.Id, m)
} }
func (r *Album) Get(id string) (*domain.Album, error) { func (r *albumRepository) Get(id string) (*domain.Album, error) {
var rec interface{} var rec interface{}
rec, err := r.readEntity(id) rec, err := r.readEntity(id)
return rec.(*domain.Album), err return rec.(*domain.Album), err

View file

@ -4,30 +4,30 @@ import (
"github.com/deluan/gosonic/domain" "github.com/deluan/gosonic/domain"
) )
type Artist struct { type artistRepository struct {
BaseRepository baseRepository
} }
func NewArtistRepository() *Artist { func NewArtistRepository() domain.ArtistRepository {
r := &Artist{} r := &artistRepository{}
r.init("artist", &domain.Artist{}) r.init("artist", &domain.Artist{})
return r return r
} }
func (r *Artist) Put(m *domain.Artist) error { func (r *artistRepository) Put(m *domain.Artist) error {
if m.Id == "" { if m.Id == "" {
m.Id = r.NewId(m.Name) m.Id = r.NewId(m.Name)
} }
return r.saveOrUpdate(m.Id, m) return r.saveOrUpdate(m.Id, m)
} }
func (r *Artist) Get(id string) (*domain.Artist, error) { func (r *artistRepository) Get(id string) (*domain.Artist, error) {
var rec interface{} var rec interface{}
rec, err := r.readEntity(id) rec, err := r.readEntity(id)
return rec.(*domain.Artist), err return rec.(*domain.Artist), err
} }
func (r *Artist) GetByName(name string) (*domain.Artist, error) { func (r *artistRepository) GetByName(name string) (*domain.Artist, error) {
id := r.NewId(name) id := r.NewId(name)
return r.Get(id) return r.Get(id)
} }

View file

@ -9,13 +9,13 @@ import (
"reflect" "reflect"
) )
type BaseRepository struct { type baseRepository struct {
table string table string
entityType reflect.Type entityType reflect.Type
fieldNames []string fieldNames []string
} }
func (r *BaseRepository) init(table string, entity interface{}) { func (r *baseRepository) init(table string, entity interface{}) {
r.table = table r.table = table
r.entityType = reflect.TypeOf(entity).Elem() r.entityType = reflect.TypeOf(entity).Elem()
@ -29,17 +29,17 @@ func (r *BaseRepository) init(table string, entity interface{}) {
} }
// TODO Use annotations to specify fields to be used // TODO Use annotations to specify fields to be used
func (r *BaseRepository) NewId(fields ...string) string { func (r *baseRepository) NewId(fields ...string) string {
s := fmt.Sprintf("%s\\%s", strings.ToUpper(r.table), strings.Join(fields, "")) s := fmt.Sprintf("%s\\%s", strings.ToUpper(r.table), strings.Join(fields, ""))
return fmt.Sprintf("%x", md5.Sum([]byte(s))) return fmt.Sprintf("%x", md5.Sum([]byte(s)))
} }
func (r *BaseRepository) CountAll() (int, error) { func (r *baseRepository) CountAll() (int, error) {
ids, err := db().SMembers([]byte(r.table + "s:all")) ids, err := db().SMembers([]byte(r.table + "s:all"))
return len(ids), err return len(ids), err
} }
func (r *BaseRepository) saveOrUpdate(id string, entity interface{}) error { func (r *baseRepository) saveOrUpdate(id string, entity interface{}) error {
recordPrefix := fmt.Sprintf("%s:%s:", r.table, id) recordPrefix := fmt.Sprintf("%s:%s:", r.table, id)
allKey := r.table + "s:all" allKey := r.table + "s:all"
@ -68,7 +68,7 @@ func (r *BaseRepository) saveOrUpdate(id string, entity interface{}) error {
} }
// TODO Optimize // TODO Optimize
func (r *BaseRepository) getParent(entity interface{}) (table string, id string) { func (r *baseRepository) getParent(entity interface{}) (table string, id string) {
dt := reflect.TypeOf(entity).Elem() dt := reflect.TypeOf(entity).Elem()
for i := 0; i < dt.NumField(); i++ { for i := 0; i < dt.NumField(); i++ {
f := dt.Field(i) f := dt.Field(i)
@ -81,7 +81,7 @@ func (r *BaseRepository) getParent(entity interface{}) (table string, id string)
return "", "" return "", ""
} }
func (r *BaseRepository) getFieldKeys(id string) [][]byte { func (r *baseRepository) getFieldKeys(id string) [][]byte {
recordPrefix := fmt.Sprintf("%s:%s:", r.table, id) recordPrefix := fmt.Sprintf("%s:%s:", r.table, id)
var fieldKeys = make([][]byte, len(r.fieldNames)) var fieldKeys = make([][]byte, len(r.fieldNames))
for i, n := range r.fieldNames { for i, n := range r.fieldNames {
@ -90,11 +90,11 @@ func (r *BaseRepository) getFieldKeys(id string) [][]byte {
return fieldKeys return fieldKeys
} }
func (r* BaseRepository) newInstance() interface{} { func (r*baseRepository) newInstance() interface{} {
return reflect.New(r.entityType).Interface() return reflect.New(r.entityType).Interface()
} }
func (r *BaseRepository) readEntity(id string) (interface{}, error) { func (r *baseRepository) readEntity(id string) (interface{}, error) {
entity := r.newInstance() entity := r.newInstance()
fieldKeys := r.getFieldKeys(id) fieldKeys := r.getFieldKeys(id)
@ -107,7 +107,7 @@ func (r *BaseRepository) readEntity(id string) (interface{}, error) {
return entity, err return entity, err
} }
func (r *BaseRepository) toEntity(response [][]byte, entity interface{}) error { func (r *baseRepository) toEntity(response [][]byte, entity interface{}) error {
var record = make(map[string]interface{}, len(response)) var record = make(map[string]interface{}, len(response))
for i, v := range response { for i, v := range response {
var value interface{} var value interface{}
@ -121,7 +121,7 @@ func (r *BaseRepository) toEntity(response [][]byte, entity interface{}) error {
} }
// TODO Optimize it! Probably very slow (and confusing!) // TODO Optimize it! Probably very slow (and confusing!)
func (r *BaseRepository) loadAll(entities interface{}, sortBy string) error { func (r *baseRepository) loadAll(entities interface{}, sortBy string) error {
total, err := r.CountAll() total, err := r.CountAll()
if (err != nil) { if (err != nil) {
return err return err

View file

@ -19,8 +19,8 @@ func shouldBeEqual(actualStruct interface{}, expectedStruct ...interface{}) stri
return ShouldEqual(actual, expected) return ShouldEqual(actual, expected)
} }
func createRepo() *BaseRepository{ func createRepo() *baseRepository {
repo := &BaseRepository{} repo := &baseRepository{}
repo.init("test", &TestEntity{}) repo.init("test", &TestEntity{})
return repo return repo
} }

View file

@ -8,7 +8,7 @@ import (
) )
type artistIndex struct { type artistIndex struct {
BaseRepository baseRepository
} }
func NewArtistIndexRepository() domain.ArtistIndexRepository { func NewArtistIndexRepository() domain.ArtistIndexRepository {

View file

@ -4,16 +4,16 @@ import (
"github.com/deluan/gosonic/domain" "github.com/deluan/gosonic/domain"
) )
type MediaFile struct { type mediaFileRepository struct {
BaseRepository baseRepository
} }
func NewMediaFileRepository() *MediaFile { func NewMediaFileRepository() domain.MediaFileRepository {
r := &MediaFile{} r := &mediaFileRepository{}
r.init("mediafile", &domain.MediaFile{}) r.init("mediafile", &domain.MediaFile{})
return r return r
} }
func (r *MediaFile) Put(m *domain.MediaFile) error { func (r *mediaFileRepository) Put(m *domain.MediaFile) error {
return r.saveOrUpdate(m.Id, m) return r.saveOrUpdate(m.Id, m)
} }

View file

@ -5,16 +5,18 @@ import (
"github.com/astaxie/beego" "github.com/astaxie/beego"
) )
type MediaFolder struct {} type mediaFolderRepository struct {
domain.MediaFolderRepository
}
func NewMediaFolderRepository() *MediaFolder { func NewMediaFolderRepository() domain.MediaFolderRepository {
return &MediaFolder{} return &mediaFolderRepository{}
} }
func (*MediaFolder) GetAll() ([]*domain.MediaFolder, error) { func (*mediaFolderRepository) GetAll() ([]domain.MediaFolder, error) {
mediaFolder := domain.MediaFolder{Id: "0", Name: "iTunes Library", Path: beego.AppConfig.String("musicFolder")} mediaFolder := domain.MediaFolder{Id: "0", Name: "iTunes Library", Path: beego.AppConfig.String("musicFolder")}
result := make([]*domain.MediaFolder, 1) result := make([]domain.MediaFolder, 1)
result[0] = &mediaFolder result[0] = mediaFolder
return result, nil return result, nil
} }

View file

@ -5,17 +5,17 @@ import (
"errors" "errors"
) )
type property struct { type propertyRepository struct {
BaseRepository baseRepository
} }
func NewPropertyRepository() *property { func NewPropertyRepository() domain.PropertyRepository {
r := &property{} r := &propertyRepository{}
r.init("property", &domain.Property{}) r.init("property", &domain.Property{})
return r return r
} }
func (r *property) Put(id string, value string) error { func (r *propertyRepository) Put(id string, value string) error {
m := &domain.Property{Id: id, Value: value} m := &domain.Property{Id: id, Value: value}
if m.Id == "" { if m.Id == "" {
return errors.New("Id is required") return errors.New("Id is required")
@ -23,13 +23,13 @@ func (r *property) Put(id string, value string) error {
return r.saveOrUpdate(m.Id, m) return r.saveOrUpdate(m.Id, m)
} }
func (r *property) Get(id string) (string, error) { func (r *propertyRepository) Get(id string) (string, error) {
var rec interface{} var rec interface{}
rec, err := r.readEntity(id) rec, err := r.readEntity(id)
return rec.(*domain.Property).Value, err return rec.(*domain.Property).Value, err
} }
func (r*property) DefaultGet(id string, defaultValue string) (string, error) { func (r*propertyRepository) DefaultGet(id string, defaultValue string) (string, error) {
v, err := r.Get(id) v, err := r.Get(id)
if v == "" { if v == "" {

View file

@ -89,7 +89,7 @@ func parseTrack(t *Track) (*domain.MediaFile, *domain.Album, *domain.Artist) {
return mf, album, artist return mf, album, artist
} }
func persist(mfRepo *persistence.MediaFile, mf *domain.MediaFile, albumRepo *persistence.Album, album *domain.Album, artistRepo *persistence.Artist, artist *domain.Artist) { func persist(mfRepo domain.MediaFileRepository, mf *domain.MediaFile, albumRepo domain.AlbumRepository, album *domain.Album, artistRepo domain.ArtistRepository, artist *domain.Artist) {
if err := artistRepo.Put(artist); err != nil { if err := artistRepo.Put(artist); err != nil {
beego.Error(err) beego.Error(err)
} }