# Architecture Patterns Skill **Description**: Common architectural patterns and structures used across the projects. ## When to Use - Designing new services or features - Onboarding to the codebase - Making architectural decisions - Understanding system design ## Core Architectures ### 1. Microservices Architecture ``` ┌─────────────────┐ ┌─────────────────┐ │ Frontend │ │ Frontend │ │ (Next.js) │ │ (Next.js) │ └─────────┬───────┘ └─────────┬───────┘ │ │ └──────────┬───────────┘ │ ┌─────────────────────┐ │ API Gateway │ │ (Traefik) │ └─────────┬───────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ┌───▼───┐ ┌─────▼─────┐ ┌───▼───┐ │Billing│ │ Platform │ │Growth │ │Service│ │ Service │ │Service│ └───────┘ └───────────┘ └───────┘ ``` **Key Principles:** - Single responsibility per service - Independent deployment - Shared data through Cosmos DB - JWT-based authentication via platform service - Product-agnostic design (productId field) ### 2. Monorepo Structure ``` learning_voice_ai_agent/ # Product repo ├── src/ # Desktop app (Python) ├── backend/ # FastAPI backend ├── admin-dashboard-web/ # Next.js admin UI ├── user-dashboard-web/ # Next.js user portal ├── tracker-dashboard-web/ # Next.js tracker UI └── mobile_app/ # Native mobile apps learning_ai_common_plat/ # Shared platform ├── packages/ # @bytelyst/* libraries │ ├── errors/ # Error types │ ├── cosmos/ # DB client │ ├── auth/ # JWT utils │ ├── config/ # Config loader │ ├── api-client/ # Fetch wrapper │ └── design-tokens/ # Design system └── services/ # @lysnrai/* microservices ├── billing-service/ ├── growth-service/ ├── platform-service/ └── tracker-service/ ``` ### 3. Mobile Architecture (MindLyst) ``` ┌─────────────────────────────────────┐ │ KMP Shared Module │ │ (Business Logic, Repositories) │ └──────────────┬──────────────────────┘ │ ┌──────────┴──────────┐ │ │ ┌───▼────┐ ┌──────▼────┐ │ Android│ │ iOS │ │(Compose)│ │ (SwiftUI) │ └────────┘ └───────────┘ ``` ## Service Patterns ### Fastify Service Template ```typescript // src/server.ts import Fastify from 'fastify'; import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'; const server = Fastify({ logger: true, }).withTypeProvider(); // Register plugins await server.register(import('@fastify/cors')); await server.register(import('./lib/auth')); // Register modules await server.register(import('./modules/auth/routes'), { prefix: '/api/auth' }); await server.register(import('./modules/users/routes'), { prefix: '/api/users' }); // Health check server.get('/health', async (request, reply) => { return { status: 'ok', service: 'billing-service', requestId: request.id, }; }); // Start server try { await server.listen({ port: 4002, host: '0.0.0.0' }); } catch (err) { server.log.error(err); process.exit(1); } ``` ### Module Pattern (types → repository → routes) ```typescript // modules/auth/types.ts import { Type } from '@sinclair/typebox'; export const LoginSchema = Type.Object({ email: Type.String({ format: 'email' }), password: Type.String({ minLength: 8 }), }); export const AuthResponseSchema = Type.Object({ token: Type.String(), user: Type.Object({ id: Type.String(), email: Type.String(), }), }); // modules/auth/repository.ts export class AuthRepository { constructor(private client: CosmosClient) {} async validateUser(email: string, password: string): Promise { // Database logic } async createToken(userId: string): Promise { // JWT creation } } // modules/auth/routes.ts import { AuthRepository } from './repository'; import { LoginSchema, AuthResponseSchema } from './types'; export default async function authRoutes(server: FastifyInstance) { const repo = new AuthRepository(server.cosmosClient); server.post( '/login', { schema: { body: LoginSchema, response: { 200: AuthResponseSchema }, }, }, async (request, reply) => { const { email, password } = request.body; const user = await repo.validateUser(email, password); if (!user) { reply.code(401); return { error: 'Invalid credentials' }; } const token = await repo.createToken(user.id); return { token, user }; } ); } ``` ## Frontend Patterns ### Next.js Dashboard Structure ``` src/app/ ├── (dashboard)/ # Layout group │ ├── layout.tsx # Dashboard layout with sidebar │ ├── page.tsx # Dashboard home │ ├── users/ │ │ ├── page.tsx # Users list │ │ └── [id]/ │ │ └── page.tsx # User detail │ └── settings/ │ └── page.tsx # Settings page ├── api/ # API routes │ ├── auth/ │ │ └── route.ts # /api/auth │ └── users/ │ └── route.ts # /api/users ├── lib/ # Utilities │ ├── cosmos.ts # @bytelyst/cosmos wrapper │ ├── auth-server.ts # @bytelyst/auth wrapper │ └── api-client.ts # Fetch wrapper └── components/ # Reusable components ├── ui/ # Base UI components └── forms/ # Form components ``` ### Service Client Pattern ```typescript // lib/billing-client.ts import { createApiClient } from '@bytelyst/api-client'; const api = createApiClient({ baseUrl: process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003', getToken: () => localStorage.getItem('token') || undefined, }); export const billingClient = { // Subscriptions getSubscriptions: () => api.fetch('/api/subscriptions'), createSubscription: (data: CreateSubscriptionDto) => api.fetch('/api/subscriptions', { method: 'POST', body: JSON.stringify(data), }), // Usage getUsage: (userId: string) => api.fetch(`/api/usage/${userId}`), }; ``` ## Data Patterns ### Cosmos DB Document Structure ```typescript // Base document interface interface BaseDocument { id: string; productId: string; // REQUIRED for multi-tenancy createdAt: string; updatedAt: string; _etag?: string; _ts?: number; } // Example: User document interface UserDocument extends BaseDocument { email: string; name: string; role: 'admin' | 'user' | 'viewer'; permissions?: string[]; } // Example: Tracker item document interface TrackerItemDocument extends BaseDocument { title: string; description: string; type: 'feature' | 'bug' | 'task'; status: 'planned' | 'in-progress' | 'completed'; visibility: 'public' | 'internal'; voteCount: number; } ``` ### Repository Pattern ```typescript // repositories/base.ts export abstract class BaseRepository { constructor( protected client: CosmosClient, protected database: string, protected container: string ) {} protected get container() { return this.client.database(this.database).container(this.container); } async create(item: Omit): Promise { const document: T = { ...item, id: crypto.randomUUID(), createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; const { resource } = await this.container.items.create(document); return resource!; } async getById(id: string, productId: string): Promise { const { resource } = await this.container.item(id, productId).read(); return resource || null; } async update(id: string, productId: string, updates: Partial): Promise { const { resource } = await this.container.item(id, productId).replace({ ...updates, id, productId, updatedAt: new Date().toISOString(), } as T); return resource!; } } ``` ## Authentication Patterns ### JWT Flow ``` ┌─────────┐ Login ┌─────────────┐ Validate ┌─────────────┐ │ Frontend │───────▶│ Platform │────────────▶│ Other │ │ │ │ Service │ │ Services │ └─────────┘◀───────└─────────────┘ └─────────────┘ Token JWT Issuer JWT Validation ``` ### Implementation ```typescript // Platform service - JWT issuance export async function loginRoute(server: FastifyInstance) { server.post('/login', async (request, reply) => { const { email, password } = request.body; // Validate credentials const user = await validateUser(email, password); if (!user) return reply.code(401).send({ error: 'Invalid credentials' }); // Issue JWT const token = await jwt.sign( { sub: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET!, { expiresIn: '24h' } ); return { token, user }; }); } // Other services - JWT validation export async function authMiddleware(server: FastifyInstance) { server.addHook('onRequest', async (request, reply) => { const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) { reply.code(401).send({ error: 'No token provided' }); return; } try { const payload = await jwt.verify(token, process.env.JWT_SECRET!); request.user = payload; } catch { reply.code(401).send({ error: 'Invalid token' }); } }); } ``` ## Configuration Patterns ### Environment-based Configuration ```typescript // lib/config.ts import { z } from 'zod'; const configSchema = z.object({ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), PORT: z.coerce.number().default(4002), COSMOS_ENDPOINT: z.string(), COSMOS_KEY: z.string(), COSMOS_DATABASE: z.string().default('lysnrai'), JWT_SECRET: z.string(), AZURE_BLOB_CONNECTION_STRING: z.string().optional(), }); export const config = configSchema.parse(process.env); ``` ### Shared Package Configuration ```typescript // @bytelyst/config export function loadProductIdentity() { const productId = process.env.PRODUCT_ID || 'lysnrai'; const env = process.env.NODE_ENV || 'development'; return { productId, env, isProduction: env === 'production', isDevelopment: env === 'development', }; } ``` ## Error Handling Patterns ### Standardized Error Types ```typescript // @bytelyst/errors export class BadRequestError extends Error { constructor( message: string, public details?: any ) { super(message); this.name = 'BadRequestError'; } } export class UnauthorizedError extends Error { constructor(message = 'Unauthorized') { super(message); this.name = 'UnauthorizedError'; } } export class NotFoundError extends Error { constructor(resource: string) { super(`${resource} not found`); this.name = 'NotFoundError'; } } ``` ### Global Error Handler ```typescript // Error handler for Fastify server.setErrorHandler((error, request, reply) => { request.log.error(error); if (error instanceof BadRequestError) { reply.code(400); return { error: error.message, details: error.details }; } if (error instanceof UnauthorizedError) { reply.code(401); return { error: error.message }; } if (error instanceof NotFoundError) { reply.code(404); return { error: error.message }; } // Default reply.code(500); return { error: 'Internal server error' }; }); ``` ## Notes - **Consistency is key** - Follow established patterns - **Product-agnostic design** - Always include productId - **Shared packages first** - Don't duplicate code - **Document decisions** - Use ADRs for major changes - **Evolve patterns** - Improve but maintain consistency ## Related Skills - [Local Development Setup](./local-development.md) - Running the architecture - [Production Readiness](./production-readiness.md) - Validating the architecture - [Debug Service](./debug-service.md) - Fixing architectural issues