2023-06-30 18:00:00+00:00

Many systems start with a unified users table containing both authentication credentials (passwords, social provider IDs) and membership data (tiers, points, profile metadata). While simple, this creates a tight coupling that makes scaling, security audits, and switching auth providers difficult.

By separating these domains into an Identity Bridge (for authentication and token verification) and a Core Membership Service (for user profiles and system permissions), you create a modular, resilient security architecture.


1. Decoupling the Database Layers

The Identity Bridge manages credentials, MFA settings, and OAuth redirects. It stores only a mapping between the login credentials and an internal Account UUID. The Core Membership Service contains the profile data, billing details, and application state keyed by the same Account UUID. This separation offers significant benefits:

2. The Synchronizer Pattern

When a user signs up via the identity system, the Identity Bridge emits a UserCreated event. The Core Membership Service listens to this event to provision a new user profile with default roles and settings:

type UserCreatedPayload struct {
    AccountID string json:"account_id"
    Email     string json:"email"
    Locale    string json:"locale"
}

func HandleUserCreated(payload UserCreatedPayload) error {
    // Provision empty profile, assign default points, create audit log
    return db.InsertProfile(payload.AccountID, payload.Email)
}

This decoupled pattern keeps security code isolated and allows membership features to scale independently.