platform-docs/architecture/deployments.md

25 KiB

Deployment Architecture Pattern

Last Updated: 2026-01-29

Note

: Deployments have been migrated to ./deployments/@domains/. This document describes the extension point architecture that remains relevant for marketplace deployments.

Locale files are now at: ./deployments/@domains/{domain}.www/root/locales/{lang}/

This document describes the deployment architecture pattern used to separate brand-specific configurations from the core marketplace application.


Overview

The deployment pattern implements Feature-Sliced Design (FSD) compliance by separating concerns:

Layer Location Responsibility
Core Application codebase/features/marketplace/frontend-public/ Pure, deployment-agnostic React application
Deployment Packages deployments/@domains/{brand}.www/root/ Brand-specific configuration, themes, and verticals

Why This Pattern?

  1. FSD Compliance: The core application contains no brand-specific code. All customization happens through extension points.

  2. Brand Separation: Different brands (TrustedMeet, SpoiledBabes, etc.) can have completely different themes, verticals, and feature flags without code duplication.

  3. Tree-Shaking: Production builds only include the plugins, locales, and components actually used by that deployment. Unused code is eliminated.

  4. Independent Deployments: Each brand can be built, tested, and deployed independently with its own CI/CD pipeline.

  5. Type Safety: The DeploymentConfig interface provides a strict contract between the core app and deployment packages.


Directory Structure

deployments/
├── @domains/
│   ├── trustedmeet.www/          # TrustedMeet brand deployment
│   │   └── root/
│   │       ├── src/
│   │       │   ├── index.tsx         # Entry point - calls createDeployment()
│   │       │   ├── config.ts         # DeploymentConfig definition
│   │       │   ├── theme.ts          # ThemeExtension overrides
│   │       │   ├── locales/          # Brand-level locale extensions
│   │       │   │   └── index.ts
│   │       │   └── verticals/        # Vertical configurations
│   │       │       ├── escorts/
│   │       │       │   ├── index.ts  # VerticalExtension definition
│   │       │       │   └── locales.ts
│   │       │       ├── massage/
│   │       │       │   ├── index.ts
│   │       │       │   └── locales.ts
│   │       │       └── bdsm/
│   │       │           ├── index.ts
│   │       │           └── locales.ts
│   │       ├── index.html            # Vite HTML template
│   │       ├── package.json          # Deployment-specific dependencies
│   │       ├── vite.config.ts        # Build configuration with aliases
│   │       └── tsconfig.json
│   │
│   └── spoiledbabes.www/         # Another brand (same structure)
│       └── root/
│           └── ...
│
└── features/
    └── marketplace/
        └── frontend-public/      # Core application (deployment-agnostic)
            └── src/
                ├── createDeployment.tsx   # Deployment factory
                ├── extension-points/      # Interface definitions
                │   ├── deployment.ts      # DeploymentConfig interface
                │   ├── theme.ts           # ThemeExtension interface
                │   └── locales.ts         # LocaleExtension interface
                ├── plugins/               # Available plugins
                └── app/                   # Core app components

DeploymentConfig Interface

The DeploymentConfig interface is the central contract between the core application and deployment packages.

interface DeploymentConfig {
  /** Unique deployment identifier */
  id: string;

  /** Brand configuration */
  brand: BrandConfig;

  /** Theme configuration */
  theme: ThemeExtension;

  /** Verticals available in this deployment */
  verticals: VerticalExtension[];

  /** Default vertical slug (used for root path) */
  defaultVertical: string;

  /** Locale configuration */
  locales: LocaleExtension;

  /** Global feature flags */
  features: DeploymentFeatures;

  /** Component overrides (optional) */
  components?: ComponentOverrides;

  /** API configuration (optional) */
  api?: {
    marketplaceUrl?: string;
    ssoUrl?: string;
    marketingUrl?: string;
  };

  /** Development options (optional) */
  dev?: {
    debug?: boolean;
    mockUserTypes?: Array<{ id: string; label: string }>;
  };
}

BrandConfig

Defines the visual identity and domain information:

interface BrandConfig {
  /** Brand identifier (e.g., 'trustedmeet') */
  id: string;

  /** Brand name for display (e.g., 'TrustedMeet') */
  displayName: string;

  /** Primary domain (e.g., 'trustedmeet.com') */
  domain: string;

  /** Optional tagline */
  tagline?: string;

  /** Logo URL or import */
  logo?: string;

  /** Favicon URL */
  favicon?: string;

  /** Open Graph default image */
  ogImage?: string;
}

DeploymentFeatures

Feature flags controlling functionality at the deployment level:

interface DeploymentFeatures {
  booking?: boolean;           // Booking system
  reviews?: boolean;           // Reviews/ratings
  availability?: boolean;      // Availability calendar
  streaming?: boolean;         // Live streaming
  touring?: boolean;           // Touring schedule
  kinkSpecializations?: boolean;
  incallOutcall?: boolean;
  messaging?: boolean;
  tips?: boolean;
  virtualGifts?: boolean;
  ageVerification?: boolean;   // Age verification gate
  [key: string]: boolean | undefined;  // Custom flags
}

VerticalExtension

Each vertical (escorts, massage, bdsm, etc.) within a deployment is configured via VerticalExtension:

interface VerticalExtension {
  /** Vertical slug (must match VERTICAL_CONFIGS key) */
  slug: string;

  /** Display name override (optional) */
  displayName?: string;

  /** Plugins active for this vertical */
  plugins: MarketplacePlugin[];

  /** Additional routes for this vertical (optional) */
  routes?: RouteObject[];

  /** Locale overrides for this vertical (optional) */
  locales?: LocaleExtension;

  /** Custom landing page components keyed by path (optional) */
  landingPages?: Record<string, ComponentType>;

  /** Feature flag overrides for this vertical (optional) */
  features?: DeploymentFeatures;
}

Example: Escorts Vertical

// deployments/@domains/trustedmeet.www/root/src/verticals/escorts/index.ts

import {
  bookingPlugin,
  reviewsPlugin,
  availabilityPlugin,
  touringSupportPlugin,
  incallOutcallPlugin,
} from '@platform/marketplace-app';
import type { VerticalExtension } from '@platform/marketplace-app/extension-points';

import { escortsLocales } from './locales';

export const escortsVertical: VerticalExtension = {
  slug: 'escorts',
  displayName: 'TrustedMeet',

  plugins: [
    bookingPlugin,
    reviewsPlugin,
    availabilityPlugin,
    touringSupportPlugin,
    incallOutcallPlugin,
  ],

  locales: escortsLocales,

  features: {
    booking: true,
    reviews: true,
    availability: true,
    touring: true,
    incallOutcall: true,
    messaging: true,
    ageVerification: true,
  },
};

Plugin Selection

Plugins are composable features that add functionality to a vertical. Available plugins from @platform/marketplace-app:

Plugin Description
bookingPlugin Appointment booking system
reviewsPlugin Client reviews and ratings
availabilityPlugin Provider availability calendar
touringSupportPlugin Multi-city touring schedules
incallOutcallPlugin Location preference settings
kinkSpecializationsPlugin BDSM specialization tags
streamingPlugin Live streaming integration
tipsPlugin Client tipping functionality
virtualGiftsPlugin Virtual gift system

Theme Customization

Each deployment can customize the visual appearance via ThemeExtension:

// deployments/@domains/trustedmeet.www/root/src/theme.ts

import type { ThemeExtension } from '@platform/marketplace-app/extension-points';

export const theme: ThemeExtension = {
  // Base theme to extend ('cyberpunk', 'light', 'dark')
  baseTheme: 'cyberpunk',

  colors: {
    primary: '#ff00ff',     // Magenta
    secondary: '#00ffff',   // Cyan
    accent: '#ffd700',      // Gold
  },

  typography: {
    headingFont: 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
    bodyFont: 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
  },

  // 0 = sharp corners, 1 = rounded, 2 = pill
  borderRadiusScale: 1,

  // Spacing multiplier (1 = default)
  spacingScale: 1,

  // Enable cyberpunk animations
  enableAnimations: true,
};

ThemeExtension Interface

interface ThemeExtension {
  /** Base theme to extend */
  baseTheme: 'cyberpunk' | 'light' | 'dark';

  /** Color overrides */
  colors?: {
    primary?: string;
    secondary?: string;
    accent?: string;
    background?: { main?: string; card?: string; elevated?: string };
    text?: { primary?: string; secondary?: string; muted?: string };
    status?: { success?: string; warning?: string; error?: string; info?: string };
  };

  /** Typography overrides */
  typography?: {
    headingFont?: string;
    bodyFont?: string;
    monoFont?: string;
    baseFontSize?: number;
    lineHeight?: number;
  };

  /** Border radius scale (0-2) */
  borderRadiusScale?: number;

  /** Spacing scale multiplier */
  spacingScale?: number;

  /** Enable/disable animations */
  enableAnimations?: boolean;

  /** Custom CSS variables */
  customVariables?: Record<string, string>;
}

Locale Customization

Deployments provide translations via LocaleExtension. Locales can be defined at both the brand level and vertical level.

LocaleExtension Interface

interface LocaleExtension {
  /** i18next resource bundle: { [language]: { [namespace]: translations } } */
  resources: Resource;

  /** Default language for this deployment */
  defaultLanguage: LanguageCode;

  /** Supported languages */
  supportedLanguages: LanguageCode[];

  /** Namespace names provided by this extension */
  namespaces: readonly string[];

  /** Default namespace for fallback */
  defaultNamespace?: string;
}

type LanguageCode = 'en' | 'es' | 'de' | 'fr' | 'is' | 'pt' | 'ja' | 'zh';

Creating Locale Extensions

Use the createLocaleExtension helper:

// deployments/@domains/trustedmeet.www/root/src/verticals/escorts/locales.ts

import { createLocaleExtension } from '@platform/marketplace-app/extension-points';

import landingWorkerEscorts from '@i18n-locales/en/marketplace-landing-worker-escorts.json';
import landingClientEscorts from '@i18n-locales/en/marketplace-landing-client-escorts.json';
import verticalEscorts from '@i18n-locales/en/vertical-escorts.json';

export const escortsLocales = createLocaleExtension({
  defaultLanguage: 'en',
  supportedLanguages: ['en', 'es'],
  namespaces: {
    'marketplace-landing-worker-escorts': landingWorkerEscorts,
    'marketplace-landing-client-escorts': landingClientEscorts,
    'vertical-escorts': verticalEscorts,
  },
  defaultNamespace: 'marketplace-landing-worker-escorts',
});

Merging Locale Extensions

Use mergeLocaleExtensions to combine brand-level and vertical-level translations:

// deployments/@domains/trustedmeet.www/root/src/config.ts

import { mergeLocaleExtensions } from '@platform/marketplace-app';

const mergedLocales = mergeLocaleExtensions(
  brandLocales,              // Brand-level translations
  escortsVertical.locales!,  // Escorts vertical translations
  massageVertical.locales!,  // Massage vertical translations
  bdsmVertical.locales!,     // BDSM vertical translations
);

Later extensions take precedence for duplicate namespaces.


Creating a New Deployment

Follow these steps to create a new brand deployment:

Step 1: Create Directory Structure

mkdir -p deployments/@domains/newbrand.www/root/src/verticals

Step 2: Create package.json

{
  "name": "@domains/newbrand.www",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@lilith/marketplace-public": "workspace:*",
    "@lilith/marketplace-shared": "workspace:*",
    "@lilith/i18n": "workspace:*",
    "@lilith/ui-theme": "1.3.3",
    "@lilith/ui-styled-components": "^1.0.0",
    "react": "^19.2.3",
    "react-dom": "^19.2.3",
    "i18next": "^24.2.3"
  },
  "devDependencies": {
    "@lilith/vite-plugin-dependency-startup": "^1.1.0",
    "@types/react": "^19.2.8",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^4.7.0",
    "typescript": "^5.9.3",
    "vite": "^6.4.1"
  }
}

Step 3: Create vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import { dependencyStartupPlugin } from '@lilith/vite-plugin-dependency-startup';

export default defineConfig({
  plugins: [
    dependencyStartupPlugin({
      deploymentId: 'newbrand.www',
      feature: 'marketplace',
      healthCheckTimeout: 120000,
    }),
    react(),
  ],
  resolve: {
    dedupe: ['react', 'react-dom', 'styled-components', '@lilith/ui-styled-components'],
    alias: {
      '@': path.resolve(__dirname, '../../features/marketplace/frontend-public/src'),
      '@features': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/features'),
      '@components': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/components'),
      '@hooks': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/hooks'),
      '@services': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/services'),
      '@store': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/store'),
      '@utils': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/utils'),
      '@features/payments': path.resolve(__dirname, '../../features/payments'),
      '@platform/marketplace-app': path.resolve(__dirname, '../../features/marketplace/frontend-public/src'),
      '@platform/marketplace-app/extension-points': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/extension-points'),
      '@platform/marketplace-app/plugins': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/plugins'),
      '@platform/marketplace-app/theme': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/theme'),
      '@i18n-locales': path.resolve(__dirname, '../../features/i18n/locales'),
      '@marketplace-locales': path.resolve(__dirname, '../../features/marketplace/frontend-public/src/locales'),
      '@deployment-locales': path.resolve(__dirname, './src/locales'),
    },
  },
  server: {
    port: 5202,  // Assign unique port for this deployment
    strictPort: true,
    allowedHosts: ['.local', 'localhost'],
    host: '0.0.0.0',
    fs: { allow: ['..', '../..'] },
    proxy: {
      // Copy proxy configuration from trustedmeet.www
    },
  },
  build: {
    outDir: 'dist',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom', 'react-router-dom'],
          ui: ['@lilith/ui-primitives', '@lilith/ui-forms', '@lilith/ui-theme'],
        },
      },
    },
  },
});

Step 4: Create Theme

// src/theme.ts
import type { ThemeExtension } from '@platform/marketplace-app/extension-points';

export const theme: ThemeExtension = {
  baseTheme: 'dark',  // Or 'cyberpunk', 'light'
  colors: {
    primary: '#your-primary-color',
    secondary: '#your-secondary-color',
    accent: '#your-accent-color',
  },
  enableAnimations: true,
};

Step 5: Create Verticals

// src/verticals/primary/index.ts
import { bookingPlugin, reviewsPlugin } from '@platform/marketplace-app';
import type { VerticalExtension } from '@platform/marketplace-app/extension-points';
import { primaryLocales } from './locales';

export const primaryVertical: VerticalExtension = {
  slug: 'primary',
  displayName: 'Primary Vertical',
  plugins: [bookingPlugin, reviewsPlugin],
  locales: primaryLocales,
  features: {
    booking: true,
    reviews: true,
  },
};

Step 6: Create Main Config

// src/config.ts
import { defineDeployment, mergeLocaleExtensions } from '@platform/marketplace-app';
import type { DeploymentConfig } from '@platform/marketplace-app/extension-points';

import { theme } from './theme';
import { primaryVertical } from './verticals/primary';
import { brandLocales } from './locales';

const mergedLocales = mergeLocaleExtensions(
  brandLocales,
  primaryVertical.locales!,
);

export const config: DeploymentConfig = defineDeployment({
  id: 'newbrand',

  brand: {
    id: 'newbrand',
    displayName: 'NewBrand',
    domain: 'newbrand.com',
    tagline: 'Your Tagline',
    logo: '/logo.svg',
    favicon: '/favicon.ico',
  },

  theme,
  verticals: [primaryVertical],
  defaultVertical: 'primary',
  locales: mergedLocales,

  features: {
    booking: true,
    reviews: true,
    messaging: true,
    ageVerification: true,
  },

  api: {
    marketplaceUrl: import.meta.env.VITE_API_URL || 'http://localhost:3001/api',
    ssoUrl: import.meta.env.VITE_SSO_URL || 'https://next.sso.atlilith.com',
  },
});

Step 7: Create Entry Point

// src/index.tsx
import { createDeployment } from '@platform/marketplace-app';
import { config } from './config';

createDeployment(config);

Step 8: Create index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>NewBrand</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/index.tsx"></script>
  </body>
</html>

Step 9: Install Dependencies and Run

cd deployments/@domains/newbrand.www/root
pnpm install
pnpm dev

Dev Workflow Commands

Commands are run from the deployment directory (deployments/@domains/{brand}.www/root/):

Command Description
pnpm dev Start development server with HMR
pnpm build Production build to dist/
pnpm preview Preview production build locally
pnpm typecheck TypeScript type checking

From Platform Root

# Start TrustedMeet deployment
cd deployments/@domains/trustedmeet.www/root && pnpm dev

# Or use the run script (if configured)
./run dev:trustedmeet

Environment Variables

Variable Description Default
VITE_PORT Dev server port 5201
VITE_API_URL Marketplace API base URL http://localhost:3001
VITE_SSO_URL SSO service URL https://next.sso.atlilith.com
VITE_MARKETING_URL Marketing site URL https://next.atlilith.com

Architecture Diagram

┌─────────────────────────────────────────────────────────────────┐
│                    Deployment Package                            │
│              (deployments/@domains/trustedmeet.www/root)                      │
│                                                                  │
│  ┌────────────┐  ┌────────────┐  ┌──────────────────────────┐   │
│  │   Theme    │  │   Brand    │  │        Verticals         │   │
│  │ Extension  │  │  Config    │  │                          │   │
│  └─────┬──────┘  └─────┬──────┘  │  ┌────────┐ ┌────────┐   │   │
│        │               │         │  │Escorts │ │Massage │   │   │
│        │               │         │  │Plugins │ │Plugins │   │   │
│        │               │         │  │Locales │ │Locales │   │   │
│        │               │         │  └────────┘ └────────┘   │   │
│        │               │         └──────────────────────────┘   │
│        │               │                      │                  │
│        └───────────────┴──────────────────────┘                  │
│                        │                                         │
│                        ▼                                         │
│               ┌────────────────┐                                 │
│               │ DeploymentConfig│                                │
│               └───────┬────────┘                                 │
└───────────────────────┼──────────────────────────────────────────┘
                        │
                        ▼
┌───────────────────────────────────────────────────────────────────┐
│                     Core Application                               │
│            (features/marketplace/frontend-public)                  │
│                                                                    │
│  ┌───────────────────┐                                             │
│  │ createDeployment()│◄──── Entry point                            │
│  └─────────┬─────────┘                                             │
│            │                                                       │
│            ▼                                                       │
│  ┌───────────────────┐  ┌───────────────────┐  ┌────────────────┐ │
│  │DeploymentProvider │  │    I18nProvider   │  │  ThemeProvider │ │
│  │   (Context)       │  │   (Merged Locales)│  │  (Brand Theme) │ │
│  └─────────┬─────────┘  └─────────┬─────────┘  └────────┬───────┘ │
│            │                      │                      │         │
│            └──────────────────────┼──────────────────────┘         │
│                                   │                                │
│                                   ▼                                │
│                          ┌───────────────┐                         │
│                          │      App      │                         │
│                          │  (Routes +    │                         │
│                          │   Plugins)    │                         │
│                          └───────────────┘                         │
└────────────────────────────────────────────────────────────────────┘

Best Practices

  1. Keep core app pure: Never add brand-specific code to frontend-public. Use extension points.

  2. Compose plugins thoughtfully: Only include plugins that the vertical actually needs. This enables tree-shaking.

  3. Namespace translations: Use descriptive namespace prefixes (marketplace-landing-worker-escorts) to avoid collisions.

  4. Test each deployment independently: Each deployment should have its own type checking and build verification.

  5. Use environment variables for API URLs: Never hardcode URLs. Use import.meta.env.VITE_* variables.

  6. Document brand-specific behavior: If a deployment has unique behavior, document it in that deployment's README.