Skip to main content

System Architecture Overview

VCEcom is built using a modular, scalable architecture that separates concerns while maintaining high performance and developer productivity. This document provides a comprehensive overview of the system architecture, design patterns, and technical decisions.

High-Level Architecture

Architectural Principles

1. Modular Design

  • Separation of Concerns: Each module has a single responsibility
  • Dependency Injection: Loose coupling through DI containers
  • Interface Segregation: Clear contracts between modules

2. Domain-Driven Design

  • Business Logic Isolation: Domain logic separated from infrastructure
  • Aggregate Roots: Well-defined transactional boundaries
  • Value Objects: Immutable domain concepts

3. Event-Driven Architecture

  • Domain Events: Business events trigger side effects
  • Eventual Consistency: Asynchronous processing where appropriate
  • Pub/Sub Pattern: Redis-based event distribution

4. CQRS Pattern

  • Command Side: Write operations with business logic validation
  • Query Side: Optimized read operations with caching
  • Separation: Different models for read and write operations

Core Modules

Authentication Module

Purpose: User authentication and authorization

Components:

  • JWT token management
  • Password hashing and validation
  • Role-based access control (RBAC)
  • Session management

Key Classes:

@Injectable()
export class AuthService {
async validateUser(email: string, password: string): Promise<User>
async generateJwtToken(user: User): Promise<string>
async validateJwtToken(token: string): Promise<User>
}

@Injectable()
export class JwtAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean | Promise<boolean>
}

Product Catalog Module

Purpose: Product, variant, and collection management

Components:

  • Product CRUD operations
  • Variant management with inventory
  • Collection and categorization
  • Search and filtering

Database Schema:

-- Core product entities
products (id, title, description, price, gst_rate, status)
product_variants (id, product_id, sku, price, inventory, size, color)
collections (id, name, slug, description)
product_collections (product_id, collection_id)

Pricing Engine Module

Purpose: Dynamic pricing calculations

Components:

  • Customer group pricing
  • Price list management
  • Rule-based pricing engine
  • Pricing snapshots for audit

Architecture:

interface PricingEngine {
calculatePrice(
variantId: string,
customerId: string,
context: PricingContext
): Promise<PriceResult>
}

interface PricingContext {
customerGroup?: string
appliedDiscounts?: DiscountSnapshot[]
quantity?: number
}

Discount Engine Module

Purpose: Complex discount rule processing

Components:

  • Discount rule definitions
  • Rule evaluation engine
  • Priority and stacking logic
  • Usage tracking and limits

Discount Types:

  • Fixed amount discounts
  • Percentage discounts
  • Buy X get Y deals
  • Tiered pricing
  • Cart-level discounts

Bundle System Module

Purpose: Product bundle management

Components:

  • Bundle definitions with choice sets
  • Dynamic bundle pricing
  • Cart integration for bundles
  • Inventory management for bundled items

Bundle Structure:

interface Bundle {
id: string
title: string
sets: BundleSet[]
}

interface BundleSet {
title: string
minQuantity: number
maxQuantity: number
items: BundleItem[]
}

Checkout System Module

Purpose: Transactional checkout processing

Components:

  • State machine for checkout flow
  • Inventory locking and reservation
  • Payment intent creation
  • Atomic state transitions

State Machine:

CREATED → LOCKED → PAYMENT_PENDING → PAYMENT_CONFIRMED → ORDER_CREATED → COMPLETED

Orders System Module

Purpose: Order lifecycle management

Components:

  • Order creation from checkout
  • Status tracking and updates
  • Inventory consumption
  • Order reconciliation

Order States:

  • Pending → Confirmed → Processing → Shipped → Delivered
  • Cancelled, Refunded (terminal states)

Reviews System Module

Purpose: Customer review management

Components:

  • Verified purchase validation
  • Content moderation (automated and manual)
  • Review aggregation and statistics
  • Caching for performance

Data Architecture

Database Design

PostgreSQL Schema Strategy

  • Normalized Design: Third normal form for data integrity
  • Indexing Strategy: Composite indexes for common query patterns
  • Constraints: Foreign keys and check constraints for data validation
  • Partitioning: Time-based partitioning for large tables (orders, reviews)

Key Tables

-- Core business entities
customers (id, email, name, phone, created_at)
products (id, title, price, gst_rate, status, created_at)
product_variants (id, product_id, sku, price, inventory, created_at)
orders (id, customer_id, order_number, status, total, created_at)
order_items (id, order_id, variant_id, quantity, price, created_at)
payments (id, order_id, amount, status, provider, created_at)

-- Supporting entities
discounts (id, code, type, value, rules, created_at)
bundles (id, title, description, sets, created_at)
reviews (id, customer_id, variant_id, rating, body, status, created_at)

Caching Architecture

Redis Multi-Layer Strategy

Layer 1: Operational Cache

  • TTL: Seconds to minutes
  • Purpose: Real-time operational data
  • Examples: Inventory levels, checkout state, payment intents

Layer 2: Application Cache

  • TTL: Hours to days
  • Purpose: Business data performance optimization
  • Examples: Product details, discount rules, bundle definitions

Layer 3: Session Store

  • TTL: Days to weeks
  • Purpose: User state persistence
  • Examples: Shopping carts, user preferences

Cache Key Patterns

// Structured key naming convention
const KEY_PATTERNS = {
// {store}:{type}:{identifier}
PRODUCT_DETAILS: (id: string) => `product:details:${id}`,
CART_CUSTOMER: (id: string) => `cart:customer:${id}`,
CHECKOUT_SESSION: (id: string) => `checkout:session:${id}`,
DISCOUNT_RULES: () => 'discount:rules',
INVENTORY_VARIANT: (id: string) => `inventory:variant:${id}`,
};

API Architecture

RESTful Design Principles

  • Resource-Based URLs: /api/v1/products/{id}
  • HTTP Methods: GET, POST, PUT, PATCH, DELETE
  • Status Codes: Standard HTTP status codes
  • Content Negotiation: JSON responses with proper content types

API Structure

├── /api/v1/admin/          # Admin operations
│ ├── products/ # Product management
│ ├── orders/ # Order management
│ ├── customers/ # Customer management
│ └── analytics/ # Business analytics
└── /api/v1/store/ # Storefront operations
├── products/ # Product browsing
├── cart/ # Shopping cart
├── checkout/ # Checkout process
└── account/ # Customer account

Authentication & Authorization

  • JWT Tokens: Stateless authentication
  • Role-Based Access: Admin vs Storefront permissions
  • API Keys: External service authentication
  • Rate Limiting: Request throttling by endpoint and user

Error Handling & Resilience

Error Classification

enum ErrorType {
VALIDATION_ERROR = 'VALIDATION_ERROR', // Input validation failures
BUSINESS_LOGIC_ERROR = 'BUSINESS_LOGIC_ERROR', // Business rule violations
INFRASTRUCTURE_ERROR = 'INFRASTRUCTURE_ERROR', // Database, cache, external service failures
AUTHENTICATION_ERROR = 'AUTHENTICATION_ERROR', // Auth failures
AUTHORIZATION_ERROR = 'AUTHORIZATION_ERROR', // Permission failures
}

Error Response Format

{
"error": {
"code": "INSUFFICIENT_INVENTORY",
"message": "Not enough inventory for product variant",
"details": {
"variantId": "variant-123",
"requested": 5,
"available": 2
},
"requestId": "req-1234567890",
"timestamp": "2024-01-15T10:30:00Z"
}
}

Circuit Breaker Pattern

@Injectable()
export class CircuitBreakerService {
private readonly circuits = new Map<string, CircuitState>();

async execute<T>(
serviceName: string,
operation: () => Promise<T>
): Promise<T> {
const circuit = this.getCircuitState(serviceName);

if (circuit.state === 'OPEN') {
throw new ServiceUnavailableError(serviceName);
}

try {
const result = await operation();
this.recordSuccess(serviceName);
return result;
} catch (error) {
this.recordFailure(serviceName);
throw error;
}
}
}

Observability Architecture

Logging Strategy

  • Structured Logging: JSON format with consistent fields
  • Log Levels: DEBUG, INFO, WARN, ERROR
  • Correlation IDs: Request tracing across services
  • Sensitive Data Redaction: Automatic PII removal

Distributed Tracing

  • OpenTelemetry: Industry-standard tracing
  • Span Hierarchy: Request → Service → Operation
  • Context Propagation: Trace context across async operations
  • Sampling: Configurable trace sampling rates

Metrics & Monitoring

  • Application Metrics: Response times, error rates, throughput
  • Business Metrics: Conversion rates, order values, inventory levels
  • Infrastructure Metrics: CPU, memory, database connections
  • Custom Dashboards: Business-specific KPI tracking

Security Architecture

Authentication Layers

// Multi-layer authentication
@Injectable()
export class AuthenticationManager {
// Primary authentication
async authenticate(credentials: Credentials): Promise<Token>

// Token validation
async validateToken(token: string): Promise<User>

// MFA support (future)
async validateMFA(userId: string, code: string): Promise<boolean>

// Session management
async invalidateSession(sessionId: string): Promise<void>
}

Authorization Matrix

// Role-based permissions
const PERMISSIONS = {
ADMIN: {
products: ['create', 'read', 'update', 'delete'],
orders: ['read', 'update', 'refund'],
customers: ['read', 'update'],
analytics: ['read'],
},
STORE: {
products: ['read'],
cart: ['create', 'read', 'update', 'delete'],
orders: ['read', 'create'],
account: ['read', 'update'],
},
};

Data Protection

  • Encryption at Rest: Sensitive data encryption in database
  • TLS in Transit: All external communications encrypted
  • Secure Headers: HTTP security headers
  • Input Validation: Comprehensive input sanitization

Deployment Architecture

Container Strategy

# Multi-stage build for optimization
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

Environment Configuration

// Environment-based configuration
@Injectable()
export class ConfigService {
private readonly config = {
database: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME || 'vcecom',
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
},
redis: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379'),
password: process.env.REDIS_PASSWORD,
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || '24h',
},
};

get<K extends keyof typeof this.config>(key: K): typeof this.config[K] {
return this.config[key];
}
}

Horizontal Scaling

  • Stateless Design: No server-side session state
  • Database Scaling: Read replicas and connection pooling
  • Cache Scaling: Redis Cluster for distributed caching
  • Load Balancing: Round-robin distribution across instances

Performance Optimization

Database Optimization

  • Connection Pooling: PgBouncer for connection management
  • Query Optimization: EXPLAIN analysis and proper indexing
  • Read Replicas: Separate read and write workloads
  • Query Caching: Redis caching for expensive queries

Caching Strategy

  • Cache-Aside Pattern: Application manages cache population
  • Write-Through: Updates go through cache to database
  • Background Refresh: Proactive cache warming
  • Intelligent TTL: Context-aware expiration times

Async Processing

@Injectable()
export class AsyncProcessorService {
// Queue non-critical operations
async queueOperation(operation: AsyncOperation): Promise<void> {
await redis.lpush('async-queue', JSON.stringify(operation));
}

// Background processing
@Cron('*/30 * * * * *') // Every 30 seconds
async processQueue(): Promise<void> {
const operations = await redis.lrange('async-queue', 0, 9);
// Process operations in batches
}
}

Development & Testing

Development Environment

  • Hot Reload: NestJS CLI with automatic restart
  • Database Migrations: Drizzle for schema management
  • Seed Data: Development data seeding
  • Debug Configuration: Source maps and debugger support

Testing Strategy

// Unit tests
describe('ProductService', () => {
let service: ProductService;
let mockRepository: MockType<Repository<Product>>;

beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [ProductService, mockRepository],
}).compile();

service = module.get<ProductService>(ProductService);
});
});

// Integration tests
describe('Order Creation Flow', () => {
it('should create order from checkout', async () => {
// Full integration test with database
});
});

// E2E tests
describe('Checkout API', () => {
it('should complete checkout flow', async () => {
// End-to-end API testing
});
});

This architecture provides a solid foundation for scalable, maintainable, and performant ecommerce applications while maintaining developer productivity and operational excellence.