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.