Scaling to Teams: Integrating B2B Multi-Tenancy with Existing B2C Architectures

On the landing project, which serves as a core platform for our users, we recently embarked on a significant architectural evolution. The goal was to seamlessly integrate B2B (business) multi-tenant support alongside our established B2C (personal) model, without compromising backward compatibility for existing users. This presented a classic challenge: how do you add complex team features, shared subscriptions, and role-based access to a system originally built for individuals?

Initially, our platform catered exclusively to individual users, each with their own subscription and dedicated subdomain. The transition to B2B demanded support for companies with shared subdomains, multiple team members, and a consolidated subscription. We needed to differentiate between these tenant types while ensuring both could coexist and thrive.

The Hybrid Tenancy Challenge

The core of the challenge lay in designing a flexible system that could understand the nuances of both tenant models. A B2C user might manage their own subscription, while a B2B user's subscription status would be inherited from their company's tenant. This required careful planning around user-tenant relationships, subscription management, and access control.

We introduced a TenantType enum (e.g., Personal, Business) to clearly differentiate tenant models. This enum became a foundational piece for various parts of the application, allowing logic to adapt based on whether a tenant was an individual or a company.

Architecting for Teams and Roles

To handle the complexities of B2B teams, we introduced several new components:

  • TenantUserRole Enum: Defining roles like Owner, Admin, and Member was crucial for granular permission management within a business tenant.
  • tenant_users Pivot Table: This new many-to-many relationship table linked users to tenants, storing their specific role within that tenant.
  • TenantMembershipService: A dedicated service for managing team members, including adding, removing, changing roles, and transferring ownership.
  • TenantInvitationService: A robust invitation system was implemented, handling email notifications, invitation expiration, and the process of accepting invitations and assigning initial roles.

Subscriptions also had to adapt. B2B tenant members now inherit their subscription status from the overarching business tenant, simplifying billing and access for companies.

Role-Based Access with Middleware

A critical aspect of multi-tenant applications is ensuring users only access resources appropriate for their role within their current tenant. This is where the Middleware Pattern shone. We developed an EnsureTenantRole middleware to enforce role-based access control across routes. This middleware inspects the user's role within the active tenant and allows or denies access based on the required roles for a given route.

Here’s a simplified illustration of how such a middleware might look in a PHP application:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use App\Enums\TenantUserRole;

class EnsureTenantRole
{
    public function handle(Request $request, Closure $next, string $requiredRoles)
    {
        $roles = explode('|', $requiredRoles);

        // Assume we can retrieve the current tenant and user context
        $currentTenant = app('tenant.manager')->currentTenant();
        $currentUser = $request->user();

        if (!$currentTenant || !$currentUser) {
            // Handle unauthenticated or no-tenant context
            return redirect('/login');
        }

        // Check if user has any of the required roles in the current tenant
        foreach ($roles as $role) {
            if ($currentTenant->userHasRole($currentUser, TenantUserRole::from($role))) {
                return $next($request);
            }
        }

        abort(403, 'Unauthorized: Insufficient role within tenant.');
    }
}

This middleware can then be applied to routes, specifying the roles required, like middleware('tenant.role:Owner|Admin').

The Takeaway

Integrating B2B multi-tenancy into an existing B2C application is a substantial undertaking but highly rewarding. The key is to introduce distinct tenant types and roles early, leverage services for managing complex flows like invitations and memberships, and utilize patterns like middleware for robust access control. By carefully segmenting logic and responsibilities, you can build a flexible platform that scales from individuals to large teams without breaking what already works.

Scaling to Teams: Integrating B2B Multi-Tenancy with Existing B2C Architectures
GERARDO RUIZ

GERARDO RUIZ

Author

Share: