Architecture Overview
The Vertical Template follows a Backend-for-Frontend (BFF) pattern where the Next.js server acts as a secure proxy between the browser and backend APIs.
System Architecture
Key Concepts
1. Backend-for-Frontend (BFF) Pattern
The Next.js server acts as a secure proxy between the browser and backend APIs:
- Security: Access tokens never exposed to browser JavaScript
- CORS: Same-origin requests avoid cross-origin restrictions
- Simplicity: Client code doesn't handle token management
All API calls flow through the catch-all route at /api/eai/[[...rest]]/route.ts.
2. Config-Driven UI
The UI is dynamically rendered from TypeScript configuration files:
// src/eai.config/tenants/my-tenant.config.ts
export const myTenantConfig: EAIConfig = {
tenantId: 'my-tenant',
displayName: 'My Application',
store: {
user: { initialState: { name: null }, persist: true },
},
layout: {
header: [{ component: 'Header', priority: 1 }],
middlePane: [{ component: 'Dashboard', priority: 1 }],
},
};
Benefits:
- No code changes for different tenants
- Rapid prototyping via configuration
- Type-safe configuration with TypeScript
3. Multi-Tenant Architecture
Each tenant/customer gets isolated configuration:
- Unique
tenantIdidentifier - Custom branding and theming
- Tenant-specific layout and components
- Separate store slices and state
Switch tenants via ?tenant=tenant-id query parameter.
4. Zustand State Management
A unified store created dynamically from tenant config:
- Dot-notation access:
useStoreValue('user.profile.email') - Selective persistence: Per-slice
persist: true/false - DevTools integration: Named actions visible in Redux DevTools
Request Flow
Authentication Flow
- Session storage: HTTP-only cookie (JWT strategy)
- Token storage: Encrypted in session, never exposed to client
- Refresh: Automatic via Auth.js when token expires
Anonymous User Support
The template supports anonymous users via Client Credentials Flow:
- When no user session exists, proxy falls back to client credentials
- App authenticates with Entra using its own credentials
- Each app registration has one anonymous user associated with it
- Anonymous users have limited privileges (read-only, public data)
Use cases:
- Public landing pages fetching non-sensitive data
- Chat endpoint for unauthenticated users
- Pre-authentication content loading
Integration Points
| Service | Purpose | Endpoint Pattern |
|---|---|---|
| Public API | Orchestration | /api/eai/v3/orchestrate |
| EAI CMS | Data persistence | Via Public API |
| Azure AI | AI operations | Via Public API |
| Chat | Conversational AI | /api/eai/v3/chat (SSE streaming) |
Related Documentation
- Project Structure — Directory layout
- Data Flow — State management patterns
- Architecture Decisions — ADRs
- Configuration — Tenant configuration
Platform & Services
- Platform Architecture — Backend services and how they connect
- Platform Services — PublicAPI, Configurator, ResourceAPI, AICore, Authz
- Authentication Guide — Entra ID, tokens, and session management
- API Reference — SDK, Hooks, and REST API documentation