Multi-Tenancy Refactoring in the Reimpact Platform
The Reimpact platform is undergoing a refactoring to better support multi-tenancy across its various modules. This involves isolating tenant-specific data and ensuring proper data access controls are in place. The recent changes focus on standardizing how models handle tenant information and streamlining queries. This post will explore the steps taken to implement tenant isolation and the benefits of these changes.
Tenant Isolation with Traits
To ensure data isolation, a HasTenantTable trait is being introduced. This trait prefixes table names with the module name, providing a clear separation of data for different tenants. This approach ensures that each tenant's data is stored in a distinct table, preventing accidental data leakage or modification.
Here's an example of how the HasTenantTable trait can be used in a model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Traits\HasTenantTable;
class CompanyReportSubscription extends Model
{
use HasTenantTable;
protected $table = 'company_report_subscriptions';
}
The trait automatically handles table naming conventions, ensuring that the table name is correctly prefixed with the module and tenant identifier.
Streamlining Queries
Previously, queries often included explicit company_id checks. With the introduction of the HasTenantTable trait and corresponding database migrations, these checks are no longer necessary. The tenant context is now automatically handled by the framework, simplifying queries and reducing the risk of errors. For example, a query that previously looked like this:
<?php
$sales = Sale::where('company_id', $companyId)->get();
can now be simplified to:
<?php
$sales = Sale::all();
This not only makes the code cleaner but also improves performance by leveraging database indexes more effectively.
Database Migrations
As part of this refactoring, new migrations are being added to handle tenant-specific tables. These migrations create separate tables for each tenant, ensuring data isolation. Additionally, migrations are being added to drop legacy tables that are no longer needed.
Here's an example of a migration for creating a tenant-specific table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAeeCompanyReportSubscriptionsTable extends Migration
{
public function up()
{
Schema::create('aee_company_report_subscriptions', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('aee_company_report_subscriptions');
}
}
Actionable Takeaway
If you're working on a multi-tenant application, consider using traits and database migrations to enforce tenant isolation. This will not only improve the security of your application but also make it easier to manage and maintain.