-
Notifications
You must be signed in to change notification settings - Fork 19
Multitenancy #231
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
base: staging
Are you sure you want to change the base?
Multitenancy #231
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces multitenancy support across multiple modules by adding tenant context propagation, repository, service, and domain logic.
- Adds tenant context to the authentication middleware
- Implements tenant service and repository for CRUD operations
- Updates domain models and repository mappers to incorporate tenant IDs
Reviewed Changes
Copilot reviewed 13 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
pkg/middleware/auth.go | Adds tenant lookup in authentication flow |
pkg/constants/middleware.go | Introduces a TenantKey constant |
pkg/composables/tenant.go | Provides helper methods for using tenant data |
modules/core/services/tenant_service.go | Implements tenant service methods |
modules/core/module.go | Registers tenant and tab services; duplicate tab service registration observed |
modules/core/infrastructure/persistence/user_repository.go | Adds tenant_id handling in user repository records |
modules/core/infrastructure/persistence/tenant_repository.go | Implements tenant repository with query methods |
modules/core/infrastructure/persistence/models/models.go | Adds tenant fields to various models |
modules/core/infrastructure/persistence/core_mappers.go | Maps tenant_id for user entity conversions |
modules/core/domain/entities/tenant/* | Defines tenant entity and repository interface |
modules/core/domain/aggregates/user/user.go | Introduces tenantID in the user aggregate |
Files not reviewed (7)
- modules/bichat/infrastructure/persistence/schema/bichat-schema.sql: Language not supported
- modules/core/infrastructure/persistence/schema/core-schema.sql: Language not supported
- modules/crm/infrastructure/persistence/schema/crm-schema.sql: Language not supported
- modules/finance/infrastructure/persistence/schema/finance-schema.sql: Language not supported
- modules/hrm/infrastructure/persistence/schema/hrm-schema.sql: Language not supported
- modules/logging/infrastructure/persistence/schema/logging-schema.sql: Language not supported
- modules/warehouse/infrastructure/persistence/schema/warehouse-schema.sql: Language not supported
Comments suppressed due to low confidence (1)
modules/core/module.go:67
- Duplicate registration of TabService is detected; consider removing the extra registration to avoid potential unexpected behavior.
services.NewTabService(persistence.NewTabRepository()),
@thelissimus-work Use UUID for tenants table. We will be migrating to UUID pretty soon, so why not start using UUID for new tables already |
Co-authored-by: Copilot <[email protected]>
Default Project
|
Project |
Default Project
|
Branch Review |
sdk-213
|
Run status |
|
Run duration | 00m 23s |
Commit |
|
Committer | Bektosh Madaminov |
View all properties for this run ↗︎ |
Test results | |
---|---|
|
0
|
|
0
|
|
0
|
|
0
|
|
3
|
View all changes introduced in this branch ↗︎ |
Extract tenant id and set it for the user in repository update Issues: #102
@@ -126,8 +137,14 @@ func (g *GormUploadRepository) Count(ctx context.Context) (int64, error) { | |||
if err != nil { | |||
return 0, err | |||
} | |||
|
|||
tenant, err := composables.UseTenant(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to require tenantId as an explicit parameter in function signatures where it is mandatory, instead of implicitly relying on its presence within a context object (ctx).
- Error Prevention: If tenantId is mandatory for the function's logic, requiring it in the signature prevents runtime errors that occur when it's missing from the ctx. This shifts error detection earlier in the development cycle.
- Reduced Boilerplate: This approach eliminates the repetitive code currently needed in many methods and repositories to extract and validate the tenantId from the context.
- Improved Clarity & Explicitness: Making tenantId an explicit parameter clearly communicates the function's dependency, making the code easier to understand and maintain, thereby reducing the potential for runtime errors.
Handling Multi-Tenant vs. All-Tenant Queries:
For scenarios where a method needs to retrieve records across all tenants (i.e., be tenant-independent), we could handle this by passing a designated value for the tenantId parameter (e.g., an empty string, null, or a specific constant). The method implementation would then check this parameter's value to decide whether or not to include the tenant filtering condition in its database query.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is indeed correct. I pointed this out to Diyor as well, for some reason tenant id propagation is done like this throughout the whole repo.
I'm reworking this whole thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mirolimsgd Regarding this issue.
It's been quite some time since this PR is hanging and because it's massive, the longer it halts, the more problems like merge conflicts and it's getting hard maintaining this PR. So maybe I could rework tenant id propagation in a different PR. I'd create a separate issue for it.
It's just that it's taking some time to do this. Besides, speeding up this with AI turned out to be a bit of problem, as it turns out AI still struggles with big refactorings across many files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bektosh-iota This wasn't implemented by me, so implement it as you see fit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the problem now. Tenant is already part of domain entities and db models, so passing it through context makes no sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mirolimsgd Regarding this issue. It's been quite some time since this PR is hanging and because it's massive, the longer it halts, the more problems like merge conflicts and it's getting hard maintaining this PR. So maybe I could rework tenant id propagation in a different PR. I'd create a separate issue for it. It's just that it's taking some time to do this. Besides, speeding up this with AI turned out to be a bit of problem, as it turns out AI still struggles with big refactorings across many files.
It is just suggestions. I am not blocking pr. The problem I see if it is merged it then it will be used and if not immediately fixed will create more work later so I suggest to change it now. It will reduce amount of code in this pr and later there is no need to spend more time.
Both options work for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mirolimsgd Regarding this issue. It's been quite some time since this PR is hanging and because it's massive, the longer it halts, the more problems like merge conflicts and it's getting hard maintaining this PR. So maybe I could rework tenant id propagation in a different PR. I'd create a separate issue for it. It's just that it's taking some time to do this. Besides, speeding up this with AI turned out to be a bit of problem, as it turns out AI still struggles with big refactorings across many files.
It is just suggestions. I am not blocking pr. The problem I see if it is merged it then it will be used and if not immediately fixed will create more work later so I suggest to change it now. It will reduce amount of code in this pr and later there is no need to spend more time. Both options work for me.
I agree
sessionUpdateQuery = ` | ||
UPDATE sessions | ||
SET expires_at = $1, | ||
ip = $2, | ||
user_agent = $3 | ||
WHERE token = $4` | ||
sessionDeleteQuery = `DELETE FROM sessions WHERE token = $1` | ||
WHERE token = $4 AND tenant_id = $5` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? Token is already a primary key
WHERE token = $4` | ||
sessionDeleteQuery = `DELETE FROM sessions WHERE token = $1` | ||
WHERE token = $4 AND tenant_id = $5` | ||
sessionDeleteQuery = `DELETE FROM sessions WHERE token = $1 AND tenant_id = $2` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here as well
var count int64 | ||
if err := tx.QueryRow(ctx, sessionCountQuery).Scan(&count); err != nil { | ||
if err := tx.QueryRow(ctx, sessionCountQuery+" WHERE tenant_id = $1", tenant.ID).Scan(&count); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For constructing sql queries in the runtime use methods from the repo
package. Reference other usages of this package to get an idea on how to use it
"github.com/google/uuid" | ||
) | ||
|
||
type Tenant struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a particular reason Tenant isn't an interface like other domain entities?
if err := tx.Commit(ctx); err != nil { | ||
t.Fatal(err) | ||
// Rollback instead of commit to ensure clean state | ||
// This is safer as it ensures tests don't affect each other |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They already don't affect each other since each test runs in a separate db. Committing changes is useful to be able to introspect the db state after a failed test
ErrNoTenantFound = errors.New("no tenant found in context") | ||
) | ||
|
||
type Tenant struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this duplicate type for?
No description provided.