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