Before we put the focus on criticism of the OP's code organisation, it's
well worth investigating the likely code bug first.

>but when I use var m storage.ModelInterface and then call a member
function of the interface (SetName) Go panics because nil pointer
dereference
Based on this problem description and the code snippet you provided, it
looks like you are referencing the storage interface incorrectly. `var m
storage.UserModelInterface` will declare a variable `m` of type
`storage.UserModelInterface` and assign to it a zero value (nil). I believe
what you meant to do was reference the store attached to the `UserService`
instance. In that case, the correct reference would be `s.storage.SetName`.

On Mon, May 29, 2017 at 5:24 AM Egon <egonel...@gmail.com> wrote:

> 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.
>

-- 
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