Overview
OrgStack is designed as a multi-tenant platform where multiple organizations will share the same application infrastructure while maintaining complete data isolation. Each organization (tenant) will operate independently with its own users, roles, and data.Planned multi-tenant architecture
The planned architecture will use a shared database, shared schema approach with tenant discrimination at the application layer. This will provide cost efficiency while ensuring strict tenant boundaries through code-level isolation.Every entity in OrgStack includes a tenant identifier to ensure data is properly scoped to the correct organization.
Tenant isolation strategy
OrgStack enforces tenant isolation through multiple layers:Request context
When you authenticate, your JWT token contains the organization ID. Spring Security extracts this and stores it in the request context.
Service layer filtering
All service methods automatically filter data by the current tenant. You never query across tenant boundaries.
Repository constraints
JPA repositories include tenant ID in all queries. Custom query methods enforce
WHERE organizationId = :tenantId clauses.Base entity design
All entities in OrgStack extendBaseEntity, which provides foundational fields for multi-tenant operation:
BaseEntity.java
Key features
- UUID primary keys: Uses
UUIDinstead of sequential IDs to prevent ID enumeration attacks across tenants - Immutable ID: The
updatable = falseconstraint prevents ID changes after creation - Audit timestamps: Automatic
createdAtandupdatedAttracking via JPA auditing - Protected constructor: Forces ID generation at instantiation time
Tenant context propagation
The tenant context flows through your application layers:Controller layer
Controller layer
Controllers extract the organization ID from the authenticated principal (JWT claims) and pass it explicitly to service methods, or rely on a tenant context holder.
Service layer
Service layer
Services validate that all operations are scoped to the current tenant. Cross-tenant operations are explicitly forbidden and throw security exceptions.
Repository layer
Repository layer
Repositories add tenant filters to all queries. Spring Data JPA’s
@Query annotations include WHERE organizationId = :orgId clauses.Database configuration
OrgStack uses PostgreSQL with connection pooling optimized for multi-tenant workloads:application.properties
The
ddl-auto=validate setting ensures schema changes happen through controlled migrations, not automatic Hibernate updates. This is critical for multi-tenant data integrity.Tenant isolation checklist
When implementing new features, ensure tenant isolation by verifying:- Entity includes
organizationIdcolumn with NOT NULL constraint - Entity has foreign key relationship to
organizationstable - Repository queries filter by
organizationId - Service methods validate tenant ownership before operations
- Unit tests verify cross-tenant access is blocked
- Integration tests confirm tenant data isolation
Best practices
Never trust client-provided tenant IDs
Always derive the organization ID from the authenticated JWT token, never from request parameters or body.
Use explicit tenant scoping
Make tenant filtering explicit in your queries. Avoid relying solely on implicit context that might be bypassed.
Test cross-tenant scenarios
Write integration tests that attempt to access data from different tenants. These should always fail with security exceptions.
Next steps
Authentication
Learn how JWT tokens carry tenant information
Authorization
Understand role-based access control within tenants