Frontend Architecture
Frontend Architecture
Key Takeaways
- Both repos are Angular 18 —
frontendshas been upgraded from 14 to 18.2.13, matchingpeeq-mono. This removes the version gap as a unification barrier. - Massive duplication — Both repos call the same 24+
backend services, have overlapping component libraries
(
@vzlabs/uivs@vzlabs/peeq-ui), and duplicate services for celebrity, content, events, and payments. - Different UI foundations — peeq-mono uses Tailwind/DaisyUI; frontends uses Bootstrap/Angular Material. This is the primary unification blocker.
- Frontend dead code — 5 API gateways (broadcast, conference, stream, dwolla, logging) call backend services not deployed to production. ~17% of API calls target non-existent services.
- 11 brand tenants themed in peeq-mono, 6 in frontends — theming is CSS-variable-based in both, but different variable naming conventions.
Migration Decision Question
Can frontends be unified into a single Angular 18 monorepo, and what does the frontend call?
Migration Verdict
Verdict: Consolidate Complexity: XL Key Constraint: Different CSS framework foundations (Tailwind/DaisyUI vs Bootstrap/Angular Material) require component-by-component migration, not a simple merge Dependencies: Backend API stability (all frontend services depend on 24+ GraphQL gateways)
Services Inventory
| Service | Gen | Deployed? | Stack | Purpose |
|---|---|---|---|---|
| peeq-mono | 2 | Yes (mono-web) | Angular 18.2 / Nx 19.8 / Ionic 6 | Fan-facing web + mobile app |
| frontends | 2 | Yes (admin-fe, celeb-fe) | Angular 18.2 / Nx 19.6 | Admin + Expert dashboards |
| landing-page | 2 | Yes (site-maintenance) | Vanilla HTML | Tenant maintenance pages |
| celeb-keycloak-theme | 2 | Yes (identityx-26) | Freemarker/CSS | Expert login theming |
| fan-keycloak-theme | 2 | Yes (identityx-26) | Freemarker/CSS | Fan login theming |
Frontend App Matrix
| App | Repo | User Type | Port | Routes | Components |
|---|---|---|---|---|---|
| mono-web | peeq-mono | Fan/Consumer | — | 40+ | 370+ |
| mobile | peeq-mono | Fan/Consumer | — | 12+ | Shared with web |
| admin-fe | frontends | Admin | 8081 | 43+ | 461 (across all) |
| celeb-fe | frontends | Expert | 8080 | 15+ | Shared with admin |
| org-dashboard-fe | frontends | Organization | 8082 | 4 | Standalone components |
API Surface
GraphQL Gateways (24+ endpoints)
Both peeq-mono and frontends call essentially identical backend services via Apollo Client:
| API Gateway | Backend Service | peeq-mono | frontends | In Prod? |
|---|---|---|---|---|
/api/celebrity |
celebrity | Yes | Yes | Yes |
/api/fan |
fan | Yes | Yes | Yes |
/api/users |
users | Yes | Yes | Yes |
/api/content |
content | Yes | Yes | Yes |
/api/media |
media | Yes | Yes | Yes |
/api/stripe |
stripe | Yes | Yes | Yes |
/api/subscriptions |
subscriptions | Yes | Yes | Yes |
/api/wallet |
wallet | Yes | Yes | Yes |
/api/transaction |
transaction | Yes | Yes | Yes |
/api/shoutout |
shoutout | Yes | Yes | Yes |
/api/inventory |
inventory | Yes | Yes | Yes |
/api/email |
Yes | Yes | Yes | |
/api/sms |
sms | Yes | Yes | Yes |
/api/chat |
chat | Yes | Yes | Yes |
/api/notifications |
notifications | Yes | Yes | Yes |
/api/sse |
sse | Yes | Yes | Yes |
/api/messageboard |
message-board | Yes | Yes | Yes |
/api/tags |
tags | Yes | Yes | Yes |
/api/webinar |
webinar | Yes | Yes | Yes |
/api/journey |
journey | Yes | Yes | Yes |
/api/classcatalog |
class-catalog | Yes | Yes | Yes |
/api/orgmanager |
org-manager | Yes | Yes | Yes |
/api/groupprofile |
group-profile | Yes | Yes | Yes |
/api/onsiteevent |
onsite-event | No | Yes | 1/4 tenants |
/api/query |
query | Yes | Yes | 1/4 tenants |
/api/broadcast |
broadcast | Yes | Yes | No |
/api/conference |
conference | Yes | Yes | No |
/api/stream |
stream | Yes | No | No |
/api/dwolla |
dwolla | No | Yes | No |
/api/logging |
logging | No | Yes | No |
Cross-Check Result (Session 0 validation)
Mismatch: 5 of 30 API gateways (~17%) call backend services NOT deployed to production. This exceeds the 10% threshold set in the plan.
Resolution: All 5 mismatches are frontend dead code — API calls to services that were once deployed but have been removed from production (broadcast, conference, stream) or never deployed (dwolla, logging). The Session 0 service list is correct; the frontend simply hasn’t been cleaned up.
Dead code inventory: - BroadcastGateway
/ BroadcastService — broadcast never reached prod -
ConferenceGateway / ConferenceService —
conference/Jitsi not deployed - StreamGateway — peeq-stream
replaced by other services - DwollaService (frontends only)
— Dwolla SDK calls in admin-fe - LoggingService (frontends
only) — no dedicated logging service in prod
REST/Non-GraphQL Endpoints
| Endpoint Pattern | Purpose | Repo |
|---|---|---|
/api/media/multipart |
File upload (images, videos) | Both |
/api/content/multipart |
Content upload | Both |
/api/shoutout/file |
Shoutout video upload | frontends |
/api/sse/subscribe |
Real-time event stream | Both |
/api/conference/sse/subscribe/* |
Conference events | Both |
API Backward Compatibility Constraints
- All GraphQL — No REST API versioning. Changes to GraphQL schema fields would break both frontends simultaneously.
- Apollo Client config is hardcoded in both repos with identical gateway URLs. Any backend URL change requires coordinated frontend deployment.
- SSE subscription paths are shared between repos — cannot change without updating both.
- File upload multipart endpoints have specific content-type expectations.
- Keycloak token format is shared — any auth changes affect all frontends.
Data Model
No frontend-specific persistent data. All state is: - Apollo InMemoryCache — per-gateway GraphQL response cache - BehaviorSubject stores — in-memory reactive state (peeq-mono) - SessionStorage/LocalStorage — auth tokens, user preferences, tenant config - No IndexedDB or Service Worker caching observed
External Integrations
| Integration | SDK | Used In | Purpose |
|---|---|---|---|
| Keycloak | keycloak-js 26.2 | Both | OAuth2/OIDC authentication |
| Stripe | @stripe/stripe-js | Both | Payment forms |
| Mux | @mux/videojs-kit | Both | Video playback |
| Phenix RTS | @phenixrts/sdk | Both | Real-time streaming |
| 100ms | @100mslive/hms-video-store | Both | Interactive video |
| Stream Chat | stream-chat-angular | Both | In-app messaging |
| Socket.io | socket.io-client | Both | WebSocket real-time |
| Zendesk | Widget embed | Both themes | Customer support |
| Google Analytics | Script embed | fan-keycloak-theme | Consumer analytics |
| LogRocket | logrocket | frontends | Session recording |
| Plaid | @types/plaid-link | frontends | Bank linking (Dwolla) |
| Dwolla | dwolla.min.js | frontends | ACH payments (admin) |
Architecture Comparison
| Aspect | peeq-mono | frontends |
|---|---|---|
| Angular | 18.2.13 | 18.2.13 |
| Nx | 19.8.9 | 19.6.4 |
| TypeScript | 5.5.4 | 5.5.2 |
| CSS Framework | Tailwind + DaisyUI | Bootstrap + Angular Material |
| Component Lib | @vzlabs/ui (249 components) |
@vzlabs/peeq-ui (100+ modules) |
| State Management | Custom Store + BehaviorSubject | BehaviorSubject + Apollo cache |
| GraphQL Client | Apollo 3.6.9 / apollo-angular 11 | Apollo 3.11.8 / apollo-angular 7 |
| Auth | keycloak-angular 16.1 | keycloak-angular 16.1 |
| Video Player | video.js + Mux | video.js + Mux |
| Chat | stream-chat-angular 4.68 | stream-chat-angular 4.68 |
| Mobile | Ionic 6 / Capacitor 3.6 | None |
| Testing | Jest 29.5 / Cypress 13 (6 files) | Jest 29.7 (passWithNoTests) |
| Brand Tenants | 11 themes | 6 themes |
| Rich Text | suneditor | suneditor + quill + monaco |
| Date Library | luxon 3 | date-fns + moment + luxon 1 |
Key Differences
- CSS foundation — Tailwind vs Bootstrap is the biggest gap. Components can’t be shared without restyling.
- Component library naming —
@vzlabs/uivs@vzlabs/peeq-uiare separate packages with overlapping but not identical APIs. - Apollo versions — Different major versions of apollo-angular (11 vs 7).
- Date libraries — peeq-mono uses luxon 3; frontends uses date-fns + moment + luxon 1 (three libraries for the same purpose).
- Additional tools in frontends — Monaco editor, Quill, Plaid, OpenPGP.
Multi-Brand Theming
peeq-mono: 11 Tenants
libs/ui/tenants/
├── agilenetwork/ ← Primary
├── peeq/
├── braintrust/
├── monster/
├── geoffreyzakarian/
├── peeqkitchen/
├── speedofai/
├── fanfuzenil/
├── vtnil/
├── nilgameplan/
└── sd4l/
frontends: 6 Tenants
libs/peeq-ui/assets/styles/themes/
├── agilenetwork.scss
├── braintrust.scss
├── nilgameplan.scss
├── fanfuzenil.scss
├── speedofai.scss
└── vtnil.scss
Theming Mechanism (Both Repos)
- Theme compiled as separate CSS bundle with
inject: false - Environment config sets
themeStylesheetpath - App initialization dynamically loads tenant CSS
- CSS custom properties (
:rootvariables) override component styles - Brand-specific assets loaded from GCS tenant buckets
Keycloak Themes
| Theme | User Type | Key Feature |
|---|---|---|
| celeb-keycloak-theme | Expert | Google OAuth, email templates (13+), Zendesk |
| fan-keycloak-theme | Consumer | Google Analytics, T&C flows, dynamic tenant resolution |
Fan theme uses realm.getName() for dynamic tenant assets
(more scalable). Celeb theme uses hardcoded paths.
Testing Status
| Metric | peeq-mono | frontends |
|---|---|---|
| Test files | 6 | ~0 (passWithNoTests) |
| Test framework | Jest + Cypress | Jest + Cypress |
| Coverage | Near zero | Near zero |
| E2E | Configured, not populated | Configured, not populated |
Both repos have effectively zero test coverage. This is a significant risk for any migration.
Gen 1 → Gen 2 Comparison
| Aspect | Gen 1 (Legacy FE) | Gen 2 (Current) |
|---|---|---|
| Fan frontend | peeq-fan-fe (standalone) | peeq-mono/web (Nx monorepo) |
| Expert frontend | peeq-celeb-fe (standalone) | frontends/celeb-fe (Nx monorepo) |
| Admin frontend | peeq-admin-fe (standalone) | frontends/admin-fe (Nx monorepo) |
| Mobile | peeq-fan-ionic (standalone Ionic) | peeq-mono/mobile (Nx + Ionic + Capacitor) |
| Handler frontend | peeq-handler-fe (standalone) | None — handler role absorbed? |
All Gen 1 standalone frontend repos are inactive and archivable. Gen 2 is the active codebase.
Modernization Implications
Can frontends be unified?
Technically feasible, but XL complexity. The barriers are:
- CSS Framework Migration (L effort): Tailwind/DaisyUI ↔︎ Bootstrap/Material requires restyling every component. Cannot be automated.
- Component Library Merge (L effort):
@vzlabs/ui(249 components) and@vzlabs/peeq-ui(100+ modules) overlap significantly but have different APIs and styles. - Apollo Version Alignment (S effort): Upgrade frontends from apollo-angular 7 to 11, or vice versa.
- Date Library Consolidation (S effort): Pick one — luxon 3 is the best candidate. Remove moment and date-fns.
- Brand Theme Reconciliation (M effort): Merge 11 + 6 tenant themes into one system. CSS variable names may differ.
Recommended Unification Strategy
graph LR
A[Current: 2 Repos] --> B{Phase 1}
B --> C[Shared API layer<br/>Extract common services]
C --> D{Phase 2}
D --> E[Standardize on Tailwind<br/>Migrate frontends components]
E --> F{Phase 3}
F --> G[Merge into single<br/>Nx monorepo]
G --> H[Unified: 1 Repo<br/>4 apps + 1 lib]
Phase 1: Extract common service layer (GraphQL gateways, auth) into a shared library usable by both repos. This provides immediate value without restyling.
Phase 2: Migrate frontends components from Bootstrap/Material to Tailwind/DaisyUI, or vice versa. This is the bottleneck.
Phase 3: Merge into single Nx monorepo with apps: mono-web, mobile, admin-fe, celeb-fe, org-dashboard-fe and one unified component library.
What to do with org-dashboard-fe?
Only 4 pages using Angular standalone components. This is already the most modern pattern. Consider it a pilot for the unified architecture — migrate it first.
Dead Code Cleanup
Remove these dead API integrations from both repos before any migration: - BroadcastGateway / BroadcastService - ConferenceGateway / ConferenceService - StreamGateway - DwollaService (if Dwolla is confirmed inactive in Session 5) - LoggingService
Last updated: 2026-01-30 — Session 1 Review by: 2026-07-30 Staleness risk: Medium — frontend evolves with feature development