Don't put organize by structure, this is what seems to be causing the most 
of the damage, 

I would suggest implementing as:

assets/*
cmd/root.go
cmd/server.go
storage/mongo/user.go
storage/id.go
user/user.go
user/service.go
forum/server.go
main.go

User package would look like:

package user

import (
"git.icod.de/dalu/forum/server/storage"
)

type User struct {
ID             storage.ID `bson:"_id,omitempty" json:"id"`
Name           string     `bson:"name" json:"name"`
Email          string     `bson:"email" json:"email"`
Password       []byte     `bson:"password" json:"-"`
ActivationCode string     `bson:"activation_code,omitempty" 
json:"activation_code,omitempty"`
RecoveryCode   string     `bson:"recovery_code,omitempty" 
json:"recovery_code,omitempty"`
Roles          []string   `bson:"roles" json:"roles"`
}

type Storage interface {
Create(m *User) error
Update(id string, m *User) error

ReadOne(id string) (*User, error)
ReadOneBy(query map[string]interface{}) (*User, error)

ReadAll(sort string, start, limit int) ([]*User, error)
ReadAllBy(query map[string]interface{}, sort string, start, limit int) 
([]UserModelInterface, error)

Delete(id string) error
DeleteBy(query map[string]interface{}) error
}

type Service struct {
storage Storage
}

func NewService(storage Storage) *Service {
return &Service{storage}
}

func (s *Service) CreateUser(name, email, password string) error {
user := &User{}
user.Name = name
user.Email = email

// ...
return s.storage.Create(m)
}

func (s *Service) VerifyActivationCode(email string) {
// ...
}

It will make your whole code easier to manage. Names will become much nicer 
and clearer. Lots of interfaces will disappear.

For storage.ID, it can be implemented as *interface{ Set(v string); 
String() string }* or *struct { I uint64; B bson.ObjectId }* (with custom 
marshalers)...

+ Egon

On Sunday, 28 May 2017 21:40:01 UTC+3, Darko Luketic wrote:
>
> I'm stuck and I hoped it wouldn't come to that.
>
> I wanted to have interfaces for various databases aka "I wanted to support 
> multiple databases" (not debatable).
> The idea was have ModelInterface (UserModelInterface, 
> CategoryModelInterface etc) which would wrap the model with getters setters
> StorageInterface which would CRUD the modelinterfaces
> and finally services which would implement higher level and more 
> convenient functions and use storage interfaces to store data.
>
> Well up to the point where I started creating services everything went 
> mostly smooth.
> But I hoped I could keep the services database-agnostic.
> However I can't.
>
> https://github.com/dalu/forum/tree/f39df77f5003f71f08f473970b3df1fbd29a5a43
>
> as you can see in line 19 and 20
>
> https://github.com/dalu/forum/blob/f39df77f5003f71f08f473970b3df1fbd29a5a43/server/service/user.go#L19
>
> when I use a concrete model.User (aka line 20) everything works without 
> error.
> but when I use var m storage.ModelInterface and then call a member 
> function of the interface (SetName)
> Go panics because nil pointer dereference
>
> === RUN   TestNewUserService
> --- FAIL: TestNewUserService (0.00s)
> panic: runtime error: invalid memory address or nil pointer dereference 
> [recovered]
>         panic: runtime error: invalid memory address or nil pointer 
> dereference
> [signal SIGSEGV: segmentation violation code=0x1 addr=0x80 pc=0x5ba9f8]
>
> goroutine 5 [running]:
> testing.tRunner.func1(0xc420068b60)
>         /usr/lib/go/src/testing/testing.go:622 +0x29d
> panic(0x5f1520, 0x721990)
>         /usr/lib/go/src/runtime/panic.go:489 +0x2cf
>
> git.icod.de/dalu/forum/server/service.(*UserService).CreateUser(0xc420055f40, 
> 0x62f170, 0x8, 0x630677, 0xd, 0x62e844, 0x6, 0x7ffdb1d66ba1, 0xc420031f68)
>         /home/darko/go/src/
> git.icod.de/dalu/forum/server/service/user.go:19 +0x28
> git.icod.de/dalu/forum/server/service.TestNewUserService(0xc420068b60)
>         /home/darko/go/src/
> git.icod.de/dalu/forum/server/service/user_test.go:23 +0x1f9
> testing.tRunner(0xc420068b60, 0x63af98)
>         /usr/lib/go/src/testing/testing.go:657 +0x96
> created by testing.(*T).Run
>         /usr/lib/go/src/testing/testing.go:697 +0x2ca
> exit status 2
> FAIL    git.icod.de/dalu/forum/server/service   0.005s
>
> However I don't know what the problem was that I had previously an 
> interface *does have* allocated memory but not in the same way the concrete 
> model that fits the interface does so they're not compatible, which lead me 
> to some headaches.
> So when an interface has memory allocated, why can't I use it as if it has 
> memory allocated?
>
> As it is right now I'll have to dump all the StorageInterface and 
> ModelInterface interfaces because it doesn't make sense to use them since 
> I'm bound to a specific model/db at the topmost level,which is "service".
> "And this is why we can't have nice things".
> Removing those would also lower the memory footprint when querying for 
> multiple results (slices, find/readall).
> It's sad, I really wanted to, but I can't.
>
> So lesson learned:
> - keep models the way they are, conrete for the expected database. A 
> mongodb model is different from a pgsql model is different from a cassandra 
> model
> - storage/repository can be concrete and it should just do CRUD
> - interface only at the top level, aka service
> - there is no 1 fits all in Go
>
> routes <> handler <interface> service <concrete> storage/repository 
> <concrete> model
>
> effectively
> MySQLUserService
> MongoDBUserService
> CassandraUserService
> RethinkDBUserService
> but
> type UserService interface {
> //...
> }
>
> Question, is there any sane way to solve it without throwing it all away? 
> Hope dies last. Any trick to make it still work with interfaces on the 
> service level without needing to create a concrete database bound instance 
> of a model/struct?
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to