lunemec / go-clean-architecture

13 stars 1 forks source link

Should there be one repository per table? #4

Open lunemec opened 6 years ago

lunemec commented 6 years ago

Currently there is one repository interface for each DB table.

type Organization interface {
    Get(uint) (model.Organization, error)
    Save(model.Organization) (model.Organization, error)
}

type Location interface {
    Get(uint) (model.Location, error)
    Save(model.Location) (model.Location, error)
}

type Contact interface {
    Get(uint) (model.Contact, error)
    Save(model.Contact) (model.Contact, error)
}

This leads to services having too many arguments #2 . However if there'd be one big interface, it would break the principle that interfaces should have as small surface area as possible. Maybe there should not be any interfaces here at all?

thfre commented 6 years ago

Can you give an example of services with a distinct responsibility which need too many repositories injected? ImportService seems like a special example with a large surface area which can be solved differently.

lunemec commented 6 years ago

@Phr0ztByte hmm, you are correct. There aren't that many. I ran into this issue in out import service, which basically loads data into all our tables. And that is where I'd like to refactor the code to be a bit more readable.

kirsle commented 6 years ago

I think you can have the return types of your functions also be interfaces.

type Model interface {
    Get(uint) (Row, error)
    Save() (error)
}

// Row could be an empty interface if you can't think of
// any useful "methods" on your row values...
type Row interface {}

// and implementations
type User struct {
    Name string
    Password []byte
}

func (u User) Get(id uint) (Row, error) {
    return &User{}, nil
}
func (u User) Save() error {
    return nil
}

// so when your code does...
user, err := User{}.Get(5)
fmt.Printf("User name is: %s\n", user.Name)

The Go compiler should see that the Get() function for your User struct returns a User (which satisfies the Row interface, and is fine), and let you access struct properties like User.Name (which don't have anything to do with the Row) because it knows it's a User object. And same with all your other models.

Might need some tweaking but that general idea might work.

lunemec commented 6 years ago

@kirsle yes, that might work. I'll try to write a bit using this, to see if there are any problems with this. One downside I can think of is having to have type casts everywhere to discern between tables, which is not ideal.