Architecture

User Identity Domain Architecture

Last updated: 2026-02-01 | Architecture

User Identity Domain Architecture

Key Takeaways

  1. Keycloak 26.3.2 is the identity hub for all 35+ production services. Two custom SPIs: Magic Link (passwordless email/SMS via Twilio) and Session Restrictor. Any migration must preserve these.
  2. Gen 2 is a full superset of Gen 1 — no feature gaps found in celebrity or fan services. Gen 1 endpoints are preserved for backward compatibility.
  3. Single-tenant deployment model — Keycloak realm and all services are configured per-tenant (fanfuzenil). No database-level tenant isolation. Each brand requires separate deployment.
  4. CIB Seven Keycloak plugin EOL — Camunda 7 CE ends October 2025. The plugin enabling Keycloak→Camunda identity sync needs a replacement strategy.
  5. All user IDs are Keycloak UUIDs — celebrity and fan profile tables use Keycloak UUIDs as primary keys. No local identity generation.

Migration Decision Question

What is the authentication/authorization migration path?

Migration Verdict

Verdict: Upgrade Complexity: L Key Constraint: Magic Link SPI (custom Keycloak extension) must be preserved or reimplemented. CIB Seven plugin needs replacement. Dependencies: All 35+ services depend on Keycloak for JWT validation. Keycloak migration affects everything.

Services Inventory

Service Gen Deployed? Stack Purpose
identityx-26 2 Yes (4/4 tenants) Keycloak 26.3.2 OAuth2/OIDC identity provider
identityx-25 2 Yes (1/4 — agilenetwork) Keycloak 25.x Legacy Keycloak instance
users 2 Yes (4/4) Java 21 / SB 3.5.4 Keycloak admin API bridge
celebrity 2 Yes (4/4) Java 21 / SB 3.5.4 Expert profile management
fan 2 Yes (4/4) Java 21 / SB 3.5.4 Fan profile + following
cibseven-keycloak 2 Via BPM engine Plugin Camunda↔︎Keycloak identity sync

Not in Production

Service Status Notes
peeq-celebrity (Gen 1) Replaced Gen 2 celebrity is superset
peeq-fan (Gen 1) Replaced Gen 2 fan is functionally identical
peeq-keycloak Archived Replaced by keycloak repo

API Surface

Users Service (GraphQL — 15+ operations)

User Management: updateKeycloakUser, whoAmI, searchUsers Magic Link Auth: magicEmailLogin, magicSmsCode, magicSmsLogin Role Management: createRole, deleteRole, addRoleToUser, removeRoleFromUser Group Management: createGroup, deleteGroup, addUserToGroup, removeUserFromGroup Verification: verifyEmail, verifyPhone

Celebrity Service (GraphQL — 27 operations + 2 REST)

Mutations (5): createCelebrity, updateCelebProfile, createShortLink, setStoreCode, toggleCelebrityProfileDelete Queries (12): queryCelebrityProfiles, celebrity (deprecated), celebrities (deprecated), celebritiesPaged (deprecated), plus count/analytics queries Referral Codes (8): CRUD for referral codes with usage tracking REST (2): Profile/event social share pages with Open Graph metadata

Fan Service (GraphQL — 21 operations + 1 REST)

Follow System (7): followCelebrity, unFollowCelebrity, followed, followedCount, doIFollowCelebs, follower counts Index Page (4): CRUD for homepage elements (admin) Media (4): Save/remove media, getFanMedia, adminGetMedia Preferences (2): getGlobalPreferences, setGlobalPreference MailChimp (1): addUserToFanMailingList REST (1): Index page rendering

Who Calls This Domain? Who Does It Call?

graph LR
    subgraph "Identity Domain"
        KC[Keycloak 26.3]
        US[Users Service]
        CEL[Celebrity]
        FAN[Fan]
    end

    subgraph "Callers"
        FE1[peeq-mono]
        FE2[frontends]
        BPM[CIB Seven BPM]
    end

    subgraph "Called Services"
        TAGS[Tags]
        INV[Inventory]
        TRACK[Tracking]
        SSE[SSE]
        MC[MailChimp]
        TW[Twilio SMS]
    end

    FE1 -->|GraphQL| CEL & FAN & US
    FE2 -->|GraphQL| CEL & FAN & US
    FE1 & FE2 -->|OAuth2| KC
    BPM -->|ReadOnly IP| KC

    CEL -->|GraphQL| TAGS & INV
    CEL -->|REST| TRACK
    CEL -->|Keycloak Admin| KC
    FAN -->|Keycloak Admin| KC
    FAN -->|REST| MC
    US -->|Admin API| KC
    KC -->|SMS| TW

    CEL -.->|RabbitMQ| SSE
    FAN -.->|RabbitMQ| SSE
    US -.->|RabbitMQ| CEL & FAN

API Backward Compatibility Constraints

Data Model

Celebrity Profile Schema (22 Flyway migrations)

erDiagram
    celebrity_profiles {
        uuid celebrity_user_id PK
        date created_on
        string bio
        string display_name
        string first_name
        string last_name
        string metadata
        string status
        string store_code
        int display_weight
        enum celeb_profile_type
        string company_name
        string title
        string profile_video
        boolean is_deleted
        date deleted_on
        uuid deleted_by_id
    }
    celebrity_profile_image {
        uuid id PK
        uuid celebrity_user_id FK
        string url
        date created_on
        string meta_data
    }
    celebrity_profile_banner {
        uuid id PK
        uuid celebrity_user_id FK
        string url
        date created_on
    }
    referral_codes {
        uuid id PK
        string code UK
        string type
        int limit
        int used
        uuid assigned_to
    }
    celebrity_profile_shortlinks {
        uuid id PK
        uuid celebrity_user_id FK
        string name
        string body
    }
    profile_status_history {
        uuid id PK
        uuid celebrity_user_id FK
        string status
        date occurred_on
    }

    celebrity_profiles ||--o{ celebrity_profile_image : has
    celebrity_profiles ||--o{ celebrity_profile_banner : has
    celebrity_profiles ||--o{ celebrity_profile_shortlinks : has
    celebrity_profiles ||--o{ profile_status_history : tracks
    celebrity_profiles }o--o| referral_codes : uses

Fan Profile Schema (11 Flyway migrations)

erDiagram
    fan_profiles {
        uuid fan_user_id PK
        date created_on
        string metadata
        boolean fan_profile_exclusively
    }
    fan_follows {
        string id PK "MD5(fan+celeb)"
        uuid fan_user_id
        uuid celeb_user_id
        date followed_on
        string followed_metadata
        date unfollowed_on
    }
    fan_follow_changes {
        uuid id PK
        date occurred_on
        uuid fan_user_id
        uuid celeb_user_id
        string change
    }
    fan_media {
        uuid id PK
        uuid fan FK
        uuid media_id
    }
    global_preferences {
        uuid id PK
        enum type
        text preference_value
    }

    fan_profiles ||--o{ fan_follows : follows
    fan_profiles ||--o{ fan_follow_changes : audit
    fan_profiles ||--o{ fan_media : saves

Data Migration Specifics

In-Flight State

External Integrations

Integration Service Purpose Migration Impact
Twilio Keycloak (Magic Link SPI) SMS code delivery API key migration
MailChimp Fan Email list management API key + audience ID
Google Cloud Storage Celebrity Profile images/banners Bucket access
Facebook Celebrity Open Graph social sharing App ID config
Tracking Service Celebrity Short link generation URL config
Intercom Users Customer messaging Config migration

Inter-Service Communication

Synchronous (REST/GraphQL)

From To Protocol Purpose
Celebrity Tags GraphQL Tag lookup for search
Celebrity Inventory GraphQL Event details for share pages
Celebrity Tracking REST Short link creation
Celebrity Keycloak Admin API User profile sync
Fan Keycloak Admin API User info retrieval
Users Keycloak Admin API All user/role/group operations

Asynchronous (RabbitMQ)

Publisher Message Consumer Exchange
Celebrity CelebrityProfileCreated ? celebrity
Celebrity CelebrityProfileUpdated ? celebrity
Celebrity CelebrityProfileSoftDeleteFlagToggled ? celebrity
Celebrity CreateSseGroup SSE sse
Celebrity AddRoleToUser Users users
Celebrity GenerateEncryptionKeys Encryption encryption
Celebrity CreateDwollaAccount Wallet? dwolla
Celebrity UpdateUserPii Users users
Fan FanProfileCreated ? fans
Fan CelebrityFollowed ? fans
Fan CelebrityUnfollowed ? fans
Fan AddUserToSseGroup SSE sse
Users UserPiiUpdated Celebrity, Fan users
Users RoleCreated/Deleted ? users
Users GroupCreated/Deleted ? users
Keycloak KeycloakEvent (register) Fan keycloak.events

Notable: Celebrity publishes CreateDwollaAccount — evidence that Dwolla was once connected to the identity domain. Supports H2 investigation in Session 5.

Gen 1 → Gen 2 Comparison

Aspect Gen 1 Gen 2 Gap
GraphQL Framework SPQR (io.leangen) Spring Native GraphQL None — all endpoints preserved
Celebrity Profile Fields Basic (bio, name, tags) Extended (+type, company, title, video, soft delete) Gen 2 is superset
Fan Profile Fields 4 fields 4 fields (identical) None
Security Explicit KeycloakSecurityContext Implicit SecurityUtil Cleaner but functionally same
Persistence javax.persistence jakarta.persistence Jakarta namespace
Test Coverage ~0 2-3 files Both very low

No feature gaps identified. Gen 2 fully replaces Gen 1 for the identity domain.

Keycloak Migration Risk Assessment

High Risk

  1. Magic Link SPI — Custom Java code compiled into Keycloak Docker image. Must be rebuilt for any Keycloak major version upgrade. Tightly coupled to Twilio for SMS.
  2. Realm Configuration — Client secrets, redirect URIs, and scope mappings are environment-specific. Export/import via Keycloak admin API, but custom attributes may need manual verification.
  3. CIB Seven Plugin EOL — Camunda 7 CE support ends. If BPM engine is upgraded to Camunda 8 or replaced, the Keycloak identity provider plugin must be replaced.

Medium Risk

  1. Session Migration — Moving from identityx-25 to identityx-26 (already happening on agilenetwork tenant) requires session invalidation or migration.
  2. Multi-Tenant Expansion — Current single-realm model doesn’t support multi-tenant data isolation. If brands need separate user pools, architectural change is needed.

Low Risk

  1. Service JWT Validation — All services validate JWT via issuer URI. Changing Keycloak URL requires config update to all 35+ services.
  2. Database Migration — Identity domain schemas are straightforward (33 total migrations across celeb + fan). Additive changes only.

Modernization Implications

Consolidation Opportunity

Celebrity and Fan services share identical architecture (same core-lib, same patterns, same messaging). They could be consolidated into a single Profile Service with role-based differentiation. However: - Different domain complexity (celebrity has 22 migrations vs fan’s 11) - Different external integrations (celebrity: tracking/GCS; fan: MailChimp) - Consolidation risk may not justify the effort

Keycloak Strategy Options

  1. Keep Keycloak, upgrade (recommended) — Keycloak 26 is modern. Preserve Magic Link SPI. Replace CIB Seven plugin.
  2. Replace with Auth0/Okta — Would require reimplementing Magic Link, migrating all client configs, and updating 35+ services. Very high effort.
  3. Replace with custom auth — Not recommended. Keycloak provides too much out-of-box (OAuth2, OIDC, MFA, social login, user management).

Multi-Brand Architecture

The identity domain reveals H11 (multi-brand is frontend-only) is partially supported: Keycloak uses environment variables for realm config, suggesting separate deployments per tenant. No backend brand-specific business logic found in celebrity or fan services. However, GCS asset paths are tenant-specific, and MailChimp audience IDs are hardcoded.


Last updated: 2026-01-30 — Session 2 Review by: 2026-07-30 Staleness risk: Medium — Keycloak version and SPI code may evolve