vendredi 23 juillet 2021

API Design Pattern (In Go)

Problem

Let's say I have a multi tenant api that manages users. I separate my app into 3 tiers: app, business, and data layers. My app layer will take in the request for the client and pass it into my business layer (I use a struct called Service as my business layer) function to update. The func will look like this:

func (s Service) UpdateUser(ctx context.Context, tenantID int, clientID int, user users.User)
  • ctx because it's Go and I use it in my data layer
  • tenantID and clientID used to make sure this caller is authorized to update the user
  • user to obviously update the user

My problem is that this is a simple function and I have 4 params. ctx is just a given but I now have to pass tenantID and clientID into every single function I make for authz purposes. For more complex functions, this could be 6 or 7 params.

Options

1) Keep as is

Pros:

  • simple and clear

Cons:

  • redundant (every function has the same tenantID and clientID) and long function parameters

2) Make input structs

example:

func (s Service) UpdateUser(ctx context.Context, updateUserInput users.UpdateUserInput)

Pros:

  • short and simple parameters

Cons:

  • abstracting required params to an object (this means you either make a New func that takes in all these same params or you have to check every request to make sure the caller filled in the proper fields)

3) New Service each request to app layer

The app layer will call something like NewService(tenantID int, clientID int)

example:

func (s Service) UpdateUser(ctx context.Context, user users.User)

Pros:

  • funcs become simple like in option 1

Cons:

  • creating a new service each time in the app layer (ex: in http api, each endpoint invocation creates a new service)

Other options are welcome.

Final Thought

Option 3 is interesting to me. Intuitively it doesn't feel right, but is it that bad to make a new struct each request? I mean, that is already happening when a request comes in, what is just one more?

This also doesn't have to be a Go specific discussion, I would also like to know how others might do it in other languages. But I would like to know about Go.

Aucun commentaire:

Enregistrer un commentaire