I am writing a Spring Boot Webflux app which needs to be Multi-tenant. The underlying database is MongoDB and we plan on using tenantId field in each of the collections. Consequently, every query needs to be scoped with the requesting user's tenant.
To reduce bolierplate as well as to provide safety against classic forgotten WHERE tenantId = ?
problem, I am looking to
- Have a way to intercept the queries and add the filter on accountId on all read queries.
- Automatically set the tenantId on save.
- Reuse the Spring Boot JPA magic of writing just interfaces.
So if I execute roleRepository.findByName("something") in the context of a request, it should automatically scope it to the current tenant.
Is there a way using AspectJ to achieve this? I wasn't able to find much resource on this kind of multitenancy in spring boot even though it is a fairly common approach with libraries in other stacks.
So far I have created the structure as follows.
Model Structure:
public interface TenantScopedModel {
public String getAccountId();
public void setAccountId(String accountId);
}
@Data
public abstract class OptionallyTenantScopedModel implements TenantScopedModel {
@Id
@MongoId(FieldType.OBJECT_ID)
protected String id;
@Getter
@Setter
@Field(targetType = FieldType.OBJECT_ID)
@Indexed
protected String accountId;
@Getter
@Setter
protected boolean isDeleted = false;
public OptionallyTenantScopedModel() {
this.id = new ObjectId().toString();
}
}
Example Model:
@EqualsAndHashCode(callSuper = true)
@Data
@Valid
@Document(collection = "roles")
public class Role extends OptionallyTenantScopedModel {
@NotBlank
private String roleName;
@NotBlank
private boolean systemDefined = false;
}
TenantScopedRepository
@NoRepositoryBean
public interface TenantScopedRepository<T extends TenantScopedModel, ID> extends ReactiveMongoRepository<T, ID> {
}