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
Compilation bool
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 {
Id 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 {
BaseRepository
Put(m *ArtistIndex) error
Get(id string) (*ArtistIndex, error)
GetAll() ([]ArtistIndex, error)

View File

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

View File

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

View File

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

View File

@ -4,30 +4,30 @@ import (
"github.com/deluan/gosonic/domain"
)
type Artist struct {
BaseRepository
type artistRepository struct {
baseRepository
}
func NewArtistRepository() *Artist {
r := &Artist{}
func NewArtistRepository() domain.ArtistRepository {
r := &artistRepository{}
r.init("artist", &domain.Artist{})
return r
}
func (r *Artist) Put(m *domain.Artist) error {
func (r *artistRepository) Put(m *domain.Artist) error {
if m.Id == "" {
m.Id = r.NewId(m.Name)
}
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{}
rec, err := r.readEntity(id)
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)
return r.Get(id)
}

View File

@ -9,13 +9,13 @@ import (
"reflect"
)
type BaseRepository struct {
type baseRepository struct {
table string
entityType reflect.Type
fieldNames []string
}
func (r *BaseRepository) init(table string, entity interface{}) {
func (r *baseRepository) init(table string, entity interface{}) {
r.table = table
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
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, ""))
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"))
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)
allKey := r.table + "s:all"
@ -68,7 +68,7 @@ func (r *BaseRepository) saveOrUpdate(id string, entity interface{}) error {
}
// 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()
for i := 0; i < dt.NumField(); i++ {
f := dt.Field(i)
@ -81,7 +81,7 @@ func (r *BaseRepository) getParent(entity interface{}) (table string, id string)
return "", ""
}
func (r *BaseRepository) getFieldKeys(id string) [][]byte {
func (r *baseRepository) getFieldKeys(id string) [][]byte {
recordPrefix := fmt.Sprintf("%s:%s:", r.table, id)
var fieldKeys = make([][]byte, len(r.fieldNames))
for i, n := range r.fieldNames {
@ -90,11 +90,11 @@ func (r *BaseRepository) getFieldKeys(id string) [][]byte {
return fieldKeys
}
func (r* BaseRepository) newInstance() interface{} {
func (r*baseRepository) newInstance() 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()
fieldKeys := r.getFieldKeys(id)
@ -107,7 +107,7 @@ func (r *BaseRepository) readEntity(id string) (interface{}, error) {
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))
for i, v := range response {
var value interface{}
@ -121,7 +121,7 @@ func (r *BaseRepository) toEntity(response [][]byte, entity interface{}) error {
}
// 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()
if (err != nil) {
return err

View File

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

View File

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

View File

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

View File

@ -5,16 +5,18 @@ import (
"github.com/astaxie/beego"
)
type MediaFolder struct {}
type mediaFolderRepository struct {
domain.MediaFolderRepository
}
func NewMediaFolderRepository() *MediaFolder {
return &MediaFolder{}
func NewMediaFolderRepository() domain.MediaFolderRepository {
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")}
result := make([]*domain.MediaFolder, 1)
result[0] = &mediaFolder
result := make([]domain.MediaFolder, 1)
result[0] = mediaFolder
return result, nil
}

View File

@ -5,17 +5,17 @@ import (
"errors"
)
type property struct {
BaseRepository
type propertyRepository struct {
baseRepository
}
func NewPropertyRepository() *property {
r := &property{}
func NewPropertyRepository() domain.PropertyRepository {
r := &propertyRepository{}
r.init("property", &domain.Property{})
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}
if m.Id == "" {
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)
}
func (r *property) Get(id string) (string, error) {
func (r *propertyRepository) Get(id string) (string, error) {
var rec interface{}
rec, err := r.readEntity(id)
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)
if v == "" {

View File

@ -89,7 +89,7 @@ func parseTrack(t *Track) (*domain.MediaFile, *domain.Album, *domain.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 {
beego.Error(err)
}