Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-tenancy logic related to repository, usecase or handler layer? #38

Open
frederikhors opened this issue Feb 14, 2020 · 3 comments
Open

Comments

@frederikhors
Copy link

frederikhors commented Feb 14, 2020

@bxcodec thanks for your amazing work! Go is just a hobby for me and I'm having fun. I'm learning a lot from your project.

I'm trying to understand if multi-tenancy column/table based is something to be "included", if it is "related to" the repository, usecase or handler level.

Example

Adding tenant.go model like this:

package models

type Tenant struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

to other models like this:

package models

type Author struct {
	ID        int64 `json:"id"`
	TenantID  int64 `json:"tenant_id"` // <--- here
	Tenant    *Tenant // <--- here
	Name      string `json:"name"`
	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

and

package models

import "time"

type Article struct {
	ID        int64 `json:"id"`
	TenantID  int64 `json:"tenant_id"` // <--- here
	Tenant    *Tenant // <--- here
	Title     string    `json:"title" validate:"required"`
	Content   string    `json:"content" validate:"required"`
	Author    Author    `json:"author"`
	UpdatedAt time.Time `json:"updated_at"`
	CreatedAt time.Time `json:"created_at"`
}

Let's talk for example of the Store() method:

Question

Let's say my tenant_id is a field of a User struct in context on every request (authenticated by a third party middleware).

Where do you think I should do something like below? In handler, usecase or repository?

tenantID := GetTenantIDFromUserInContext()
article.TenantID = tenantID

Doubts about fetch queries

Today, before I discover the amazing "clean architecture", I'm using a where clause in my SQL queries (go-pg/pg#1179), like this:

// Used as: "q.Apply(FilterByTenant(ctx))"
func FilterByTenant(ctx context.Context) func(q *orm.Query) (*orm.Query, error) {
	user := ctx.Value(auth.CTXKeyUser).(*models.User)
	return func(q *orm.Query) (*orm.Query, error) {
		q = q.Where("tenant_id = ?", user.TenantID)
		return q, nil
	}
}

I think maybe the concept of FilterByTenant in the usecase layer is an unnecessary repetition and should belong to lower levels like repositories?

But I also think that the main multi-tenancy logic does not change with the change of possible repository types (Postgres, Mysql, Mongo, microservices).

What do you think about it?

@bxcodec
Copy link
Owner

bxcodec commented May 15, 2020

Hi @frederikhors sorry for late reply, so busy lately, hope you doing good.

I'll try to answer your question based on what I've done.

Where do you think I should do something like below? In handler, usecase or repository?

tenantID := GetTenantIDFromUserInContext()
article.TenantID = tenantID

I will put it in the usecase. Is context here means like the context.Context package?
Or something else?

Have you thought about user-repository? And that repo will be injected to the article or whatever who need the details? like a tenant or stuff?

@frederikhors
Copy link
Author

Is context here means like the context.Context package?

Yep.

Have you thought about user-repository? And that repo will be injected to the article or whatever who need the details? like a tenant or stuff?

I don't understand the question.

@bxcodec
Copy link
Owner

bxcodec commented Aug 4, 2022

Oh my god, I missed this.
So sorry @frederikhors tough time back then.

Let's schedule some time to discuss this properly. I want to learn how's your approach to the solution, it may be useful to add other use cases in this repo?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants