ASP.NET Core  

ASP.NET Core Frontend Fusion: Integrating React, Angular & Vue.js - Complete Guide (Part - 23 of 40)

frontent

 

Previous article: Blazor Superpowers - SPA Components Real Time - ASP.NET Core - Master WebApps (Part-22 of 40)

Table of contents

  1. Understanding Frontend-Backend Architecture

  2. Integration Patterns Overview

  3. React +  ASP.NET  Core Deep Integration

  4. Angular +  ASP.NET  Core Enterprise Setup

  5. Vue.js +  ASP.NET  Core Lightweight Fusion

  6. Real-time Communication Strategies

  7. State Management Across Boundaries

  8. Authentication & Authorization Flow

  9. Performance Optimization Techniques

  10. Deployment & DevOps Strategies

Table of Contents

  1. Introduction to Frontend Fusion

  2. Architecture Patterns

  3. Project Setup & Configuration

  4. React Integration Deep Dive

  5. Angular Enterprise Integration

  6. Vue.js Lightweight Integration

  7. Real-World E-Commerce Example

  8. Performance Optimization

  9. Security Considerations

  10. Deployment Strategies

1. Introduction to Frontend Fusion

The Modern Web Development Landscape

In today's web development ecosystem, the separation of frontend and backend has become a standard practice. However, seamlessly integrating these two worlds remains a challenge that many developers face. Frontend Fusion represents the art and science of blending JavaScript frameworks with  ASP.NET  Core to create powerful, maintainable, and scalable applications.

Why Frontend Fusion Matters

Traditional Approach Limitations:

  • Tight coupling between UI and business logic

  • Limited scalability

  • Poor developer experience

  • Difficulty in adopting new frontend technologies

Frontend Fusion Benefits:

  • Independent development workflows

  • Technology flexibility

  • Improved performance

  • Better team specialization

  • Enhanced user experience

Real-World Scenario: E-Commerce Platform

Imagine building an e-commerce platform where:

  • Product catalog uses React for rich interactivity

  • Admin dashboard uses Angular for enterprise features

  • Customer portal uses Vue.js for lightweight performance

  • All seamlessly integrated with  ASP.NET  Core backend

2. Architecture Patterns

2.1 Separation of Concerns Architecture

  
    // Backend Architecture
ECommerceSolution/
├── ECommerce.API/           // ASP.NET Core Web API
├── ECommerce.Application/   // Business Logic
├── ECommerce.Domain/        // Domain Models
├── ECommerce.Infrastructure/ // Data Access
└── ECommerce.Shared/        // Shared Utilities

// Frontend Architecture
frontend/
├── react-app/              // React SPA
├── angular-app/            // Angular SPA
├── vue-app/                // Vue.js SPA
└── shared-components/      // Shared UI Components
  

2.2 Micro-Frontends Architecture

  
    // ASP.NET Core serving multiple frontends
public class FrontendRoutingMiddleware
{
    private readonly RequestDelegate _next;
    
    public FrontendRoutingMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        var path = context.Request.Path.Value ?? "";
        
        if (path.StartsWith("/react"))
        {
            // Serve React app
            await ServeReactApp(context);
        }
        else if (path.StartsWith("/angular"))
        {
            // Serve Angular app
            await ServeAngularApp(context);
        }
        else if (path.StartsWith("/vue"))
        {
            // Serve Vue app
            await ServeVueApp(context);
        }
        else
        {
            await _next(context);
        }
    }
}
  

2.3 API Gateway Pattern

  
    // Program.cs - API Gateway configuration
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

// appsettings.json
{
  "ReverseProxy": {
    "Routes": {
      "react-app": {
        "ClusterId": "react-cluster",
        "Match": { "Path": "/react/{**catch-all}" }
      },
      "angular-app": {
        "ClusterId": "angular-cluster",
        "Match": { "Path": "/angular/{**catch-all}" }
      },
      "vue-app": {
        "ClusterId": "vue-cluster",
        "Match": { "Path": "/vue/{**catch-all}" }
      }
    },
    "Clusters": {
      "react-cluster": {
        "Destinations": { "react-server": { "Address": "https://localhost:3000/" } }
      },
      "angular-cluster": {
        "Destinations": { "angular-server": { "Address": "https://localhost:4200/" } }
      },
      "vue-cluster": {
        "Destinations": { "vue-server": { "Address": "https://localhost:8080/" } }
      }
    }
  }
}
  

3. Project Setup & Configuration

3.1  ASP.NET  Core Backend Setup

  
    // Program.cs - Modern minimal API setup
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();

// Database Context
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// CORS for multiple frontends
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAllFrontends", policy =>
    {
        policy.WithOrigins(
                "http://localhost:3000", // React
                "http://localhost:4200", // Angular
                "http://localhost:8080") // Vue.js
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials();
    });
});

// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"]))
        };
    });

// Swagger/OpenAPI
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "ECommerce API", Version = "v1" });
    
    // JWT Support in Swagger
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme",
        Type = SecuritySchemeType.Http,
        Scheme = "bearer"
    });
});

var app = builder.Build();

// Configure pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseCors("AllowAllFrontends");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();
  

3.2 Shared Configuration Management

  
    // appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=ECommerce;Trusted_Connection=true;TrustServerCertificate=true;"
  },
  "Jwt": {
    "Secret": "your-super-secret-key-at-least-32-characters-long",
    "Issuer": "ecommerce-api",
    "Audience": "ecommerce-apps",
    "ExpiryMinutes": 60
  },
  "Frontend": {
    "ReactAppUrl": "http://localhost:3000",
    "AngularAppUrl": "http://localhost:4200",
    "VueAppUrl": "http://localhost:8080"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

// Frontend configuration service
public class FrontendConfigService
{
    private readonly IConfiguration _configuration;
    
    public FrontendConfigService(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    
    public FrontendConfig GetConfig(string frontendType)
    {
        return frontendType.ToLower() switch
        {
            "react" => new FrontendConfig 
            { 
                ApiUrl = _configuration["ApiBaseUrl"],
                AppUrl = _configuration["Frontend:ReactAppUrl"],
                Features = new List<string> { "SSR", "PWA", "RealTime" }
            },
            "angular" => new FrontendConfig 
            { 
                ApiUrl = _configuration["ApiBaseUrl"],
                AppUrl = _configuration["Frontend:AngularAppUrl"],
                Features = new List<string> { "LazyLoading", "AOT", "Universal" }
            },
            "vue" => new FrontendConfig 
            { 
                ApiUrl = _configuration["ApiBaseUrl"],
                AppUrl = _configuration["Frontend:VueAppUrl"],
                Features = new List<string> { "CompositionAPI", "Vite", "Pinia" }
            },
            _ => throw new ArgumentException($"Unknown frontend type: {frontendType}")
        };
    }
}

public class FrontendConfig
{
    public string ApiUrl { get; set; } = string.Empty;
    public string AppUrl { get; set; } = string.Empty;
    public List<string> Features { get; set; } = new();
}
  

4. React Integration Deep Dive

4.1 React Project Setup with TypeScript

  
    // package.json for React + ASP.NET Core integration
{
  "name": "ecommerce-react-app",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "serve": "npm run build && aspnetcore-https && dotnet run"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-query": "^3.39.3",
    "axios": "^1.4.0",
    "react-router-dom": "^6.14.1",
    "zustand": "^4.3.9",
    "react-hook-form": "^7.45.1"
  },
  "devDependencies": {
    "@types/react": "^18.2.15",
    "@types/react-dom": "^18.2.7",
    "@vitejs/plugin-react": "^4.0.3",
    "typescript": "^5.0.2",
    "vite": "^4.4.5"
  }
}
  

4.2 API Service Layer

  
    // src/services/apiClient.ts
import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios';

class ApiClient {
  private client: AxiosInstance;

  constructor(baseURL: string) {
    this.client = axios.create({
      baseURL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    this.setupInterceptors();
  }

  private setupInterceptors(): void {
    // Request interceptor
    this.client.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        const token = localStorage.getItem('authToken');
        if (token && config.headers) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );

    // Response interceptor
    this.client.interceptors.response.use(
      (response: AxiosResponse) => response,
      (error) => {
        if (error.response?.status === 401) {
          localStorage.removeItem('authToken');
          window.location.href = '/login';
        }
        return Promise.reject(error);
      }
    );
  }

  public async get<T>(url: string, params?: any): Promise<T> {
    const response = await this.client.get<T>(url, { params });
    return response.data;
  }

  public async post<T>(url: string, data?: any): Promise<T> {
    const response = await this.client.post<T>(url, data);
    return response.data;
  }

  public async put<T>(url: string, data?: any): Promise<T> {
    const response = await this.client.put<T>(url, data);
    return response.data;
  }

  public async delete<T>(url: string): Promise<T> {
    const response = await this.client.delete<T>(url);
    return response.data;
  }
}

// Create API client instance
export const apiClient = new ApiClient(import.meta.env.VITE_API_BASE_URL);
  

4.3 React Hooks for API Integration

  
    // src/hooks/useApi.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '../services/apiClient';

// Product types
export interface Product {
  id: number;
  name: string;
  description: string;
  price: number;
  category: string;
  imageUrl: string;
  stock: number;
  createdAt: string;
}

export interface CreateProductRequest {
  name: string;
  description: string;
  price: number;
  category: string;
  imageUrl: string;
  stock: number;
}

// Product API hooks
export const useProducts = (category?: string) => {
  return useQuery({
    queryKey: ['products', category],
    queryFn: () => 
      apiClient.get<Product[]>('/api/products', { category }),
    staleTime: 5 * 60 * 1000, // 5 minutes
  });
};

export const useProduct = (id: number) => {
  return useQuery({
    queryKey: ['products', id],
    queryFn: () => apiClient.get<Product>(`/api/products/${id}`),
    enabled: !!id,
  });
};

export const useCreateProduct = () => {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: (product: CreateProductRequest) =>
      apiClient.post<Product>('/api/products', product),
    onSuccess: () => {
      // Invalidate and refetch products query
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });
};

export const useUpdateProduct = () => {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: ({ id, ...product }: Partial<Product> & { id: number }) =>
      apiClient.put<Product>(`/api/products/${id}`, product),
    onSuccess: (_, variables) => {
      queryClient.invalidateQueries({ queryKey: ['products'] });
      queryClient.invalidateQueries({ queryKey: ['products', variables.id] });
    },
  });
};

export const useDeleteProduct = () => {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: (id: number) =>
      apiClient.delete(`/api/products/${id}`),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });
};
  

4.4 React Components with  ASP.NET  Core Integration

  
    // src/components/ProductList.tsx
import React from 'react';
import { useProducts, useDeleteProduct } from '../hooks/useApi';
import { ProductCard } from './ProductCard';
import { LoadingSpinner } from './LoadingSpinner';
import { ErrorMessage } from './ErrorMessage';

interface ProductListProps {
  category?: string;
  onProductSelect?: (product: Product) => void;
}

export const ProductList: React.FC<ProductListProps> = ({ 
  category, 
  onProductSelect 
}) => {
  const { data: products, isLoading, error } = useProducts(category);
  const deleteProductMutation = useDeleteProduct();

  const handleDelete = async (productId: number) => {
    if (window.confirm('Are you sure you want to delete this product?')) {
      try {
        await deleteProductMutation.mutateAsync(productId);
      } catch (error) {
        console.error('Failed to delete product:', error);
      }
    }
  };

  if (isLoading) return <LoadingSpinner />;
  if (error) return <ErrorMessage message="Failed to load products" />;

  return (
    <div className="product-grid">
      {products?.map((product) => (
        <ProductCard
          key={product.id}
          product={product}
          onSelect={onProductSelect}
          onDelete={handleDelete}
          canDelete={true}
        />
      ))}
    </div>
  );
};
  

4.5 Real-time Features with SignalR

  
    // src/services/signalRService.ts
import { HubConnection, HubConnectionBuilder, LogLevel } from '@microsoft/signalr';

class SignalRService {
  private connection: HubConnection | null = null;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 5;

  async connect(hubUrl: string): Promise<void> {
    try {
      this.connection = new HubConnectionBuilder()
        .withUrl(hubUrl)
        .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
        .configureLogging(LogLevel.Information)
        .build();

      this.setupEventHandlers();

      await this.connection.start();
      console.log('SignalR Connected');
      this.reconnectAttempts = 0;
    } catch (error) {
      console.error('SignalR Connection Failed:', error);
      this.handleReconnection();
    }
  }

  private setupEventHandlers(): void {
    if (!this.connection) return;

    this.connection.onclose(() => {
      console.log('SignalR Connection Closed');
      this.handleReconnection();
    });

    this.connection.onreconnecting(() => {
      console.log('SignalR Reconnecting...');
    });

    this.connection.onreconnected(() => {
      console.log('SignalR Reconnected');
      this.reconnectAttempts = 0;
    });
  }

  private handleReconnection(): void {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      setTimeout(() => {
        this.connect(this.connection?.connection.baseUrl || '');
      }, Math.min(1000 * this.reconnectAttempts, 30000));
    }
  }

  on<T>(methodName: string, callback: (data: T) => void): void {
    this.connection?.on(methodName, callback);
  }

  async invoke<T>(methodName: string, ...args: any[]): Promise<T> {
    if (!this.connection) {
      throw new Error('SignalR connection not established');
    }
    return await this.connection.invoke<T>(methodName, ...args);
  }

  async disconnect(): Promise<void> {
    if (this.connection) {
      await this.connection.stop();
      this.connection = null;
    }
  }
}

export const signalRService = new SignalRService();
  

5. Angular Enterprise Integration

5.1 Angular Project Structure

  
    // angular.json - Enterprise configuration
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "ecommerce-angular": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/ecommerce-angular",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/favicon.ico",
              "src/assets",
              "src/web.config"
            ],
            "styles": [
              "src/styles.scss",
              "node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
            ],
            "scripts": [],
            "serviceWorker": true,
            "ngswConfigPath": "ngsw-config.json"
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kb",
                  "maximumError": "1mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "4kb"
                }
              ],
              "outputHashing": "all",
              "optimization": true,
              "sourceMap": false
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "ecommerce-angular:build:production"
            }
          }
        }
      }
    }
  }
}
  

5.2 Angular Services for  ASP.NET  Core Integration

  
    // src/app/core/services/api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { environment } from '../../../environments/environment';

export interface ApiResponse<T> {
  data: T;
  success: boolean;
  message?: string;
  totalCount?: number;
}

@Injectable({
  providedIn: 'root'
})
export class ApiService {
  private baseUrl = environment.apiUrl;

  constructor(private http: HttpClient) { }

  get<T>(endpoint: string, params?: any): Observable<T> {
    let httpParams = new HttpParams();
    if (params) {
      Object.keys(params).forEach(key => {
        if (params[key] !== null && params[key] !== undefined) {
          httpParams = httpParams.set(key, params[key].toString());
        }
      });
    }

    return this.http.get<ApiResponse<T>>(`${this.baseUrl}${endpoint}`, { params: httpParams })
      .pipe(
        map(response => response.data),
        catchError(this.handleError)
      );
  }

  post<T>(endpoint: string, data: any): Observable<T> {
    return this.http.post<ApiResponse<T>>(`${this.baseUrl}${endpoint}`, data)
      .pipe(
        map(response => response.data),
        catchError(this.handleError)
      );
  }

  put<T>(endpoint: string, data: any): Observable<T> {
    return this.http.put<ApiResponse<T>>(`${this.baseUrl}${endpoint}`, data)
      .pipe(
        map(response => response.data),
        catchError(this.handleError)
      );
  }

  delete<T>(endpoint: string): Observable<T> {
    return this.http.delete<ApiResponse<T>>(`${this.baseUrl}${endpoint}`)
      .pipe(
        map(response => response.data),
        catchError(this.handleError)
      );
  }

  private handleError(error: any) {
    let errorMessage = 'An unknown error occurred';
    
    if (error.error instanceof ErrorEvent) {
      // Client-side error
      errorMessage = `Error: ${error.error.message}`;
    } else {
      // Server-side error
      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    
    console.error(errorMessage);
    return throwError(() => new Error(errorMessage));
  }
}
  

5.3 Angular State Management with NgRx

  
    // src/app/store/product/product.actions.ts
import { createAction, props } from '@ngrx/store';
import { Product } from '../../core/models/product.model';

export const loadProducts = createAction(
  '[Product] Load Products',
  props<{ category?: string }>()
);

export const loadProductsSuccess = createAction(
  '[Product] Load Products Success',
  props<{ products: Product[] }>()
);

export const loadProductsFailure = createAction(
  '[Product] Load Products Failure',
  props<{ error: string }>()
);

export const createProduct = createAction(
  '[Product] Create Product',
  props<{ product: Omit<Product, 'id'> }>()
);

export const createProductSuccess = createAction(
  '[Product] Create Product Success',
  props<{ product: Product }>()
);

export const createProductFailure = createAction(
  '[Product] Create Product Failure',
  props<{ error: string }>()
);

// src/app/store/product/product.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { ProductService } from '../../core/services/product.service';
import * as ProductActions from './product.actions';

@Injectable()
export class ProductEffects {
  loadProducts$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ProductActions.loadProducts),
      mergeMap((action) =>
        this.productService.getProducts(action.category).pipe(
          map(products => ProductActions.loadProductsSuccess({ products })),
          catchError(error => of(ProductActions.loadProductsFailure({ error: error.message })))
        )
      )
    )
  );

  createProduct$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ProductActions.createProduct),
      mergeMap((action) =>
        this.productService.createProduct(action.product).pipe(
          map(product => ProductActions.createProductSuccess({ product })),
          catchError(error => of(ProductActions.createProductFailure({ error: error.message })))
        )
      )
    )
  );

  constructor(
    private actions$: Actions,
    private productService: ProductService
  ) {}
}
  

5.4 Angular Authentication Interceptor

  
    // src/app/core/interceptors/auth.interceptor.ts
import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError, BehaviorSubject } from 'rxjs';
import { catchError, filter, take, switchMap } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private isRefreshing = false;
  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.authService.getAccessToken();
    
    if (token) {
      request = this.addToken(request, token);
    }

    return next.handle(request).pipe(
      catchError(error => {
        if (error instanceof HttpErrorResponse && error.status === 401) {
          return this.handle401Error(request, next);
        } else {
          return throwError(() => error);
        }
      })
    );
  }

  private addToken(request: HttpRequest<any>, token: string) {
    return request.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
  }

  private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);

      return this.authService.refreshToken().pipe(
        switchMap((token: any) => {
          this.isRefreshing = false;
          this.refreshTokenSubject.next(token.accessToken);
          return next.handle(this.addToken(request, token.accessToken));
        }),
        catchError((error) => {
          this.isRefreshing = false;
          this.authService.logout();
          return throwError(() => error);
        })
      );
    } else {
      return this.refreshTokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(token => {
          return next.handle(this.addToken(request, token));
        })
      );
    }
  }
}
  

6. Vue.js Lightweight Integration

6.1 Vue 3 Composition API Integration

  
    // src/composables/useApi.ts
import { ref, reactive } from 'vue';
import type { Ref } from 'vue';
import { apiClient } from '../services/apiClient';

interface ApiState<T> {
  data: T | null;
  loading: boolean;
  error: string | null;
}

export function useApi<T>() {
  const state = reactive<ApiState<T>>({
    data: null,
    loading: false,
    error: null
  });

  const execute = async (apiCall: () => Promise<T>) => {
    state.loading = true;
    state.error = null;
    
    try {
      state.data = await apiCall();
    } catch (error) {
      state.error = error instanceof Error ? error.message : 'An error occurred';
      console.error('API Error:', error);
    } finally {
      state.loading = false;
    }
  };

  return {
    state,
    execute
  };
}

// Product-specific composable
export function useProducts() {
  const { state, execute } = useApi<Product[]>();

  const fetchProducts = async (category?: string) => {
    await execute(() => 
      apiClient.get<Product[]>('/api/products', { category })
    );
  };

  const createProduct = async (product: CreateProductRequest) => {
    await execute(() => 
      apiClient.post<Product>('/api/products', product)
    );
  };

  return {
    products: state,
    fetchProducts,
    createProduct
  };
}
  

6.2 Vuex/Pinia Store Integration

  
    // stores/products.ts - Pinia Store
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import { apiClient } from '../services/apiClient';
import type { Product, CreateProductRequest } from '../types';

export const useProductStore = defineStore('products', () => {
  // State
  const products = ref<Product[]>([]);
  const loading = ref(false);
  const error = ref<string | null>(null);
  const selectedCategory = ref<string>('');

  // Getters
  const featuredProducts = computed(() => 
    products.value.filter(product => product.price > 100)
  );

  const productsByCategory = computed(() =>
    selectedCategory.value 
      ? products.value.filter(product => product.category === selectedCategory.value)
      : products.value
  );

  const totalValue = computed(() =>
    products.value.reduce((total, product) => total + product.price, 0)
  );

  // Actions
  const fetchProducts = async (category?: string) => {
    loading.value = true;
    error.value = null;
    
    try {
      const response = await apiClient.get<Product[]>('/api/products', { category });
      products.value = response;
      selectedCategory.value = category || '';
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Failed to fetch products';
      console.error('Failed to fetch products:', err);
    } finally {
      loading.value = false;
    }
  };

  const createProduct = async (productData: CreateProductRequest) => {
    try {
      const newProduct = await apiClient.post<Product>('/api/products', productData);
      products.value.push(newProduct);
      return newProduct;
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Failed to create product';
      throw err;
    }
  };

  const updateProduct = async (productId: number, updates: Partial<Product>) => {
    try {
      const updatedProduct = await apiClient.put<Product>(
        `/api/products/${productId}`, 
        updates
      );
      
      const index = products.value.findIndex(p => p.id === productId);
      if (index !== -1) {
        products.value[index] = updatedProduct;
      }
      
      return updatedProduct;
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Failed to update product';
      throw err;
    }
  };

  const deleteProduct = async (productId: number) => {
    try {
      await apiClient.delete(`/api/products/${productId}`);
      products.value = products.value.filter(p => p.id !== productId);
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Failed to delete product';
      throw err;
    }
  };

  return {
    // State
    products,
    loading,
    error,
    selectedCategory,
    
    // Getters
    featuredProducts,
    productsByCategory,
    totalValue,
    
    // Actions
    fetchProducts,
    createProduct,
    updateProduct,
    deleteProduct
  };
});
  

6.3 Vue Components with  ASP.NET  Core Backend

  
    <!-- src/components/ProductManagement.vue -->
<template>
  <div class="product-management">
    <div class="header">
      <h2>Product Management</h2>
      <button @click="showCreateForm = true" class="btn-primary">
        Add New Product
      </button>
    </div>

    <!-- Filters -->
    <div class="filters">
      <select v-model="selectedCategory" @change="handleCategoryChange">
        <option value="">All Categories</option>
        <option v-for="category in categories" :key="category" :value="category">
          {{ category }}
        </option>
      </select>
      
      <input
        v-model="searchQuery"
        type="text"
        placeholder="Search products..."
        @input="handleSearch"
      />
    </div>

    <!-- Loading State -->
    <div v-if="productStore.loading" class="loading">
      <LoadingSpinner />
    </div>

    <!-- Error State -->
    <div v-else-if="productStore.error" class="error">
      {{ productStore.error }}
      <button @click="loadProducts" class="btn-secondary">Retry</button>
    </div>

    <!-- Products Grid -->
    <div v-else class="products-grid">
      <ProductCard
        v-for="product in filteredProducts"
        :key="product.id"
        :product="product"
        @edit="handleEdit"
        @delete="handleDelete"
      />
    </div>

    <!-- Empty State -->
    <div v-if="!productStore.loading && filteredProducts.length === 0" class="empty-state">
      <p>No products found.</p>
    </div>

    <!-- Create/Edit Modal -->
    <ProductFormModal
      v-if="showCreateForm || editingProduct"
      :product="editingProduct"
      @save="handleSave"
      @cancel="handleCancel"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { useProductStore } from '../stores/products';
import ProductCard from './ProductCard.vue';
import ProductFormModal from './ProductFormModal.vue';
import LoadingSpinner from './LoadingSpinner.vue';
import type { Product, CreateProductRequest } from '../types';

const productStore = useProductStore();
const selectedCategory = ref('');
const searchQuery = ref('');
const showCreateForm = ref(false);
const editingProduct = ref<Product | null>(null);

// Computed properties
const categories = computed(() => 
  Array.from(new Set(productStore.products.map(p => p.category)))
);

const filteredProducts = computed(() => {
  let products = productStore.productsByCategory;
  
  if (searchQuery.value) {
    const query = searchQuery.value.toLowerCase();
    products = products.filter(product =>
      product.name.toLowerCase().includes(query) ||
      product.description.toLowerCase().includes(query)
    );
  }
  
  return products;
});

// Lifecycle
onMounted(() => {
  loadProducts();
});

// Methods
const loadProducts = () => {
  productStore.fetchProducts(selectedCategory.value);
};

const handleCategoryChange = () => {
  loadProducts();
};

const handleSearch = () => {
  // Search is handled by computed property
};

const handleEdit = (product: Product) => {
  editingProduct.value = { ...product };
};

const handleDelete = async (productId: number) => {
  if (confirm('Are you sure you want to delete this product?')) {
    try {
      await productStore.deleteProduct(productId);
    } catch (error) {
      console.error('Failed to delete product:', error);
    }
  }
};

const handleSave = async (productData: CreateProductRequest | Product) => {
  try {
    if (editingProduct.value) {
      // Update existing product
      await productStore.updateProduct(
        (productData as Product).id,
        productData
      );
    } else {
      // Create new product
      await productStore.createProduct(productData as CreateProductRequest);
    }
    
    // Reset form state
    showCreateForm.value = false;
    editingProduct.value = null;
  } catch (error) {
    console.error('Failed to save product:', error);
  }
};

const handleCancel = () => {
  showCreateForm.value = false;
  editingProduct.value = null;
};
</script>

<style scoped>
.product-management {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 30px;
}

.filters {
  display: flex;
  gap: 15px;
  margin-bottom: 20px;
}

.filters select,
.filters input {
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.products-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
}

.empty-state {
  text-align: center;
  padding: 40px;
  color: #666;
}

.loading, .error {
  text-align: center;
  padding: 40px;
}

.error {
  color: #d32f2f;
}
</style>
  

7. Real-World E-Commerce Example

7.1  ASP.NET  Core Backend API Controllers

  
    // Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;

namespace ECommerce.API.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        private readonly ApplicationDbContext _context;
        private readonly ILogger<ProductsController> _logger;

        public ProductsController(
            ApplicationDbContext context, 
            ILogger<ProductsController> logger)
        {
            _context = context;
            _logger = logger;
        }

        [HttpGet]
        public async Task<ActionResult<ApiResponse<PagedResult<ProductDto>>>> GetProducts(
            [FromQuery] ProductQueryParameters parameters)
        {
            try
            {
                var query = _context.Products.AsQueryable();

                // Filter by category
                if (!string.IsNullOrEmpty(parameters.Category))
                {
                    query = query.Where(p => p.Category == parameters.Category);
                }

                // Search by name or description
                if (!string.IsNullOrEmpty(parameters.SearchTerm))
                {
                    query = query.Where(p => 
                        p.Name.Contains(parameters.SearchTerm) ||
                        p.Description.Contains(parameters.SearchTerm));
                }

                // Price range filter
                if (parameters.MinPrice.HasValue)
                {
                    query = query.Where(p => p.Price >= parameters.MinPrice.Value);
                }

                if (parameters.MaxPrice.HasValue)
                {
                    query = query.Where(p => p.Price <= parameters.MaxPrice.Value);
                }

                // Get total count for pagination
                var totalCount = await query.CountAsync();

                // Apply sorting
                query = parameters.SortBy?.ToLower() switch
                {
                    "price" => parameters.SortDescending ?? false 
                        ? query.OrderByDescending(p => p.Price)
                        : query.OrderBy(p => p.Price),
                    "name" => parameters.SortDescending ?? false
                        ? query.OrderByDescending(p => p.Name)
                        : query.OrderBy(p => p.Name),
                    "date" => parameters.SortDescending ?? false
                        ? query.OrderByDescending(p => p.CreatedAt)
                        : query.OrderBy(p => p.CreatedAt),
                    _ => query.OrderByDescending(p => p.CreatedAt)
                };

                // Apply pagination
                var products = await query
                    .Skip((parameters.Page - 1) * parameters.PageSize)
                    .Take(parameters.PageSize)
                    .Select(p => new ProductDto
                    {
                        Id = p.Id,
                        Name = p.Name,
                        Description = p.Description,
                        Price = p.Price,
                        Category = p.Category,
                        ImageUrl = p.ImageUrl,
                        Stock = p.Stock,
                        CreatedAt = p.CreatedAt
                    })
                    .ToListAsync();

                var result = new PagedResult<ProductDto>
                {
                    Items = products,
                    TotalCount = totalCount,
                    Page = parameters.Page,
                    PageSize = parameters.PageSize,
                    TotalPages = (int)Math.Ceiling(totalCount / (double)parameters.PageSize)
                };

                return Ok(ApiResponse<PagedResult<ProductDto>>.Success(result));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving products");
                return StatusCode(500, ApiResponse<object>.Failure("An error occurred while retrieving products"));
            }
        }

        [HttpGet("{id}")]
        public async Task<ActionResult<ApiResponse<ProductDto>>> GetProduct(int id)
        {
            try
            {
                var product = await _context.Products
                    .Where(p => p.Id == id)
                    .Select(p => new ProductDto
                    {
                        Id = p.Id,
                        Name = p.Name,
                        Description = p.Description,
                        Price = p.Price,
                        Category = p.Category,
                        ImageUrl = p.ImageUrl,
                        Stock = p.Stock,
                        CreatedAt = p.CreatedAt
                    })
                    .FirstOrDefaultAsync();

                if (product == null)
                {
                    return NotFound(ApiResponse<object>.Failure("Product not found"));
                }

                return Ok(ApiResponse<ProductDto>.Success(product));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving product with ID {ProductId}", id);
                return StatusCode(500, ApiResponse<object>.Failure("An error occurred while retrieving the product"));
            }
        }

        [HttpPost]
        [Authorize(Roles = "Admin")]
        public async Task<ActionResult<ApiResponse<ProductDto>>> CreateProduct(CreateProductRequest request)
        {
            try
            {
                var product = new Product
                {
                    Name = request.Name,
                    Description = request.Description,
                    Price = request.Price,
                    Category = request.Category,
                    ImageUrl = request.ImageUrl,
                    Stock = request.Stock,
                    CreatedAt = DateTime.UtcNow
                };

                _context.Products.Add(product);
                await _context.SaveChangesAsync();

                var productDto = new ProductDto
                {
                    Id = product.Id,
                    Name = product.Name,
                    Description = product.Description,
                    Price = product.Price,
                    Category = product.Category,
                    ImageUrl = product.ImageUrl,
                    Stock = product.Stock,
                    CreatedAt = product.CreatedAt
                };

                return CreatedAtAction(
                    nameof(GetProduct), 
                    new { id = product.Id }, 
                    ApiResponse<ProductDto>.Success(productDto));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating product");
                return StatusCode(500, ApiResponse<object>.Failure("An error occurred while creating the product"));
            }
        }

        [HttpPut("{id}")]
        [Authorize(Roles = "Admin")]
        public async Task<ActionResult<ApiResponse<ProductDto>>> UpdateProduct(
            int id, 
            UpdateProductRequest request)
        {
            try
            {
                var product = await _context.Products.FindAsync(id);
                if (product == null)
                {
                    return NotFound(ApiResponse<object>.Failure("Product not found"));
                }

                product.Name = request.Name ?? product.Name;
                product.Description = request.Description ?? product.Description;
                product.Price = request.Price ?? product.Price;
                product.Category = request.Category ?? product.Category;
                product.ImageUrl = request.ImageUrl ?? product.ImageUrl;
                product.Stock = request.Stock ?? product.Stock;

                await _context.SaveChangesAsync();

                var productDto = new ProductDto
                {
                    Id = product.Id,
                    Name = product.Name,
                    Description = product.Description,
                    Price = product.Price,
                    Category = product.Category,
                    ImageUrl = product.ImageUrl,
                    Stock = product.Stock,
                    CreatedAt = product.CreatedAt
                };

                return Ok(ApiResponse<ProductDto>.Success(productDto));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating product with ID {ProductId}", id);
                return StatusCode(500, ApiResponse<object>.Failure("An error occurred while updating the product"));
            }
        }

        [HttpDelete("{id}")]
        [Authorize(Roles = "Admin")]
        public async Task<ActionResult<ApiResponse<object>>> DeleteProduct(int id)
        {
            try
            {
                var product = await _context.Products.FindAsync(id);
                if (product == null)
                {
                    return NotFound(ApiResponse<object>.Failure("Product not found"));
                }

                _context.Products.Remove(product);
                await _context.SaveChangesAsync();

                return Ok(ApiResponse<object>.Success(null, "Product deleted successfully"));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting product with ID {ProductId}", id);
                return StatusCode(500, ApiResponse<object>.Failure("An error occurred while deleting the product"));
            }
        }
    }

    public class ProductQueryParameters
    {
        [Range(1, int.MaxValue)]
        public int Page { get; set; } = 1;

        [Range(1, 100)]
        public int PageSize { get; set; } = 10;

        public string? Category { get; set; }
        public string? SearchTerm { get; set; }
        public decimal? MinPrice { get; set; }
        public decimal? MaxPrice { get; set; }
        public string? SortBy { get; set; }
        public bool? SortDescending { get; set; }
    }

    public class PagedResult<T>
    {
        public List<T> Items { get; set; } = new();
        public int TotalCount { get; set; }
        public int Page { get; set; }
        public int PageSize { get; set; }
        public int TotalPages { get; set; }
    }

    public class ApiResponse<T>
    {
        public bool Success { get; set; }
        public string? Message { get; set; }
        public T? Data { get; set; }

        public static ApiResponse<T> Success(T data, string? message = null)
        {
            return new ApiResponse<T> { Success = true, Data = data, Message = message };
        }

        public static ApiResponse<T> Failure(string message)
        {
            return new ApiResponse<T> { Success = false, Message = message };
        }
    }
}
  

7.2 Real-time Inventory Management with SignalR

  
    // Hubs/InventoryHub.cs
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;

namespace ECommerce.API.Hubs
{
    public class InventoryHub : Hub
    {
        private readonly ApplicationDbContext _context;
        private static readonly Dictionary<string, string> _userGroups = new();

        public InventoryHub(ApplicationDbContext context)
        {
            _context = context;
        }

        public override async Task OnConnectedAsync()
        {
            var httpContext = Context.GetHttpContext();
            var userId = httpContext?.Request.Query["userId"].ToString();
            
            if (!string.IsNullOrEmpty(userId))
            {
                _userGroups[Context.ConnectionId] = userId;
                await Groups.AddToGroupAsync(Context.ConnectionId, $"user-{userId}");
            }

            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception? exception)
        {
            if (_userGroups.ContainsKey(Context.ConnectionId))
            {
                var userId = _userGroups[Context.ConnectionId];
                await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"user-{userId}");
                _userGroups.Remove(Context.ConnectionId);
            }

            await base.OnDisconnectedAsync(exception);
        }

        public async Task SubscribeToProduct(int productId)
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, $"product-{productId}");
        }

        public async Task UnsubscribeFromProduct(int productId)
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"product-{productId}");
        }

        public async Task UpdateStock(int productId, int newStock)
        {
            var product = await _context.Products.FindAsync(productId);
            if (product != null)
            {
                product.Stock = newStock;
                await _context.SaveChangesAsync();

                // Notify all subscribers
                await Clients.Group($"product-{productId}")
                    .SendAsync("StockUpdated", productId, newStock);
            }
        }
    }
}

// Services/InventoryNotificationService.cs
using Microsoft.AspNetCore.SignalR;

namespace ECommerce.API.Services
{
    public interface IInventoryNotificationService
    {
        Task NotifyStockUpdate(int productId, int newStock);
        Task NotifyLowStock(int productId, int currentStock);
        Task NotifyProductUpdate(int productId);
    }

    public class InventoryNotificationService : IInventoryNotificationService
    {
        private readonly IHubContext<InventoryHub> _hubContext;
        private readonly ILogger<InventoryNotificationService> _logger;

        public InventoryNotificationService(
            IHubContext<InventoryHub> hubContext,
            ILogger<InventoryNotificationService> logger)
        {
            _hubContext = hubContext;
            _logger = logger;
        }

        public async Task NotifyStockUpdate(int productId, int newStock)
        {
            try
            {
                await _hubContext.Clients.Group($"product-{productId}")
                    .SendAsync("StockUpdated", productId, newStock);
                
                _logger.LogInformation("Stock update notified for product {ProductId}", productId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error notifying stock update for product {ProductId}", productId);
            }
        }

        public async Task NotifyLowStock(int productId, int currentStock)
        {
            try
            {
                await _hubContext.Clients.Group("admin-users")
                    .SendAsync("LowStockAlert", productId, currentStock);
                
                _logger.LogWarning("Low stock alert sent for product {ProductId}", productId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending low stock alert for product {ProductId}", productId);
            }
        }

        public async Task NotifyProductUpdate(int productId)
        {
            try
            {
                await _hubContext.Clients.Group($"product-{productId}")
                    .SendAsync("ProductUpdated", productId);
                
                _logger.LogInformation("Product update notified for product {ProductId}", productId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error notifying product update for product {ProductId}", productId);
            }
        }
    }
}
  

8. Performance Optimization

8.1 Caching Strategies

  
    // Services/CachingService.cs
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

namespace ECommerce.API.Services
{
    public interface ICachingService
    {
        Task<T?> GetAsync<T>(string key);
        Task SetAsync<T>(string key, T value, TimeSpan? expiration = null);
        Task RemoveAsync(string key);
        Task<bool> ExistsAsync(string key);
    }

    public class CachingService : ICachingService
    {
        private readonly IDistributedCache _cache;
        private readonly ILogger<CachingService> _logger;

        public CachingService(IDistributedCache cache, ILogger<CachingService> logger)
        {
            _cache = cache;
            _logger = logger;
        }

        public async Task<T?> GetAsync<T>(string key)
        {
            try
            {
                var cachedData = await _cache.GetStringAsync(key);
                if (cachedData == null)
                {
                    _logger.LogDebug("Cache miss for key: {CacheKey}", key);
                    return default;
                }

                _logger.LogDebug("Cache hit for key: {CacheKey}", key);
                return JsonSerializer.Deserialize<T>(cachedData);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving cache for key: {CacheKey}", key);
                return default;
            }
        }

        public async Task SetAsync<T>(string key, T value, TimeSpan? expiration = null)
        {
            try
            {
                var options = new DistributedCacheEntryOptions();

                if (expiration.HasValue)
                {
                    options.SetAbsoluteExpiration(expiration.Value);
                }
                else
                {
                    // Default expiration: 5 minutes
                    options.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
                }

                var serializedData = JsonSerializer.Serialize(value);
                await _cache.SetStringAsync(key, serializedData, options);
                
                _logger.LogDebug("Cache set for key: {CacheKey}", key);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error setting cache for key: {CacheKey}", key);
            }
        }

        public async Task RemoveAsync(string key)
        {
            try
            {
                await _cache.RemoveAsync(key);
                _logger.LogDebug("Cache removed for key: {CacheKey}", key);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error removing cache for key: {CacheKey}", key);
            }
        }

        public async Task<bool> ExistsAsync(string key)
        {
            try
            {
                var cachedData = await _cache.GetStringAsync(key);
                return cachedData != null;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error checking cache existence for key: {CacheKey}", key);
                return false;
            }
        }
    }
}

// Cached Repository Pattern
public class CachedProductRepository : IProductRepository
{
    private readonly IProductRepository _decorated;
    private readonly ICachingService _cachingService;
    private readonly ILogger<CachedProductRepository> _logger;

    public CachedProductRepository(
        IProductRepository decorated,
        ICachingService cachingService,
        ILogger<CachedProductRepository> logger)
    {
        _decorated = decorated;
        _cachingService = cachingService;
        _logger = logger;
    }

    public async Task<Product?> GetByIdAsync(int id)
    {
        var cacheKey = $"product-{id}";
        
        var cachedProduct = await _cachingService.GetAsync<Product>(cacheKey);
        if (cachedProduct != null)
        {
            return cachedProduct;
        }

        var product = await _decorated.GetByIdAsync(id);
        if (product != null)
        {
            await _cachingService.SetAsync(cacheKey, product, TimeSpan.FromMinutes(10));
        }

        return product;
    }

    public async Task<PagedResult<Product>> GetProductsAsync(ProductQueryParameters parameters)
    {
        // Create cache key from parameters
        var cacheKey = $"products-{JsonSerializer.Serialize(parameters)}";
        
        var cachedResult = await _cachingService.GetAsync<PagedResult<Product>>(cacheKey);
        if (cachedResult != null)
        {
            return cachedResult;
        }

        var result = await _decorated.GetProductsAsync(parameters);
        await _cachingService.SetAsync(cacheKey, result, TimeSpan.FromMinutes(5));

        return result;
    }

    public async Task<Product> CreateAsync(Product product)
    {
        var result = await _decorated.CreateAsync(product);
        
        // Invalidate relevant caches
        await _cachingService.RemoveAsync("products-");
        await _cachingService.RemoveAsync($"product-{result.Id}");
        
        return result;
    }

    public async Task UpdateAsync(Product product)
    {
        await _decorated.UpdateAsync(product);
        
        // Invalidate relevant caches
        await _cachingService.RemoveAsync("products-");
        await _cachingService.RemoveAsync($"product-{product.Id}");
    }

    public async Task DeleteAsync(int id)
    {
        await _decorated.DeleteAsync(id);
        
        // Invalidate relevant caches
        await _cachingService.RemoveAsync("products-");
        await _cachingService.RemoveAsync($"product-{id}");
    }
}
  

8.2 Frontend Performance Optimization

  
    // React Performance Optimization with React.memo and useMemo
import React, { memo, useMemo, useCallback } from 'react';

interface ProductCardProps {
  product: Product;
  onSelect: (product: Product) => void;
  onDelete: (productId: number) => void;
  canDelete: boolean;
}

export const ProductCard = memo<ProductCardProps>(({ 
  product, 
  onSelect, 
  onDelete, 
  canDelete 
}) => {
  const handleSelect = useCallback(() => {
    onSelect(product);
  }, [onSelect, product]);

  const handleDelete = useCallback(() => {
    onDelete(product.id);
  }, [onDelete, product.id]);

  const formattedPrice = useMemo(() => {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD'
    }).format(product.price);
  }, [product.price]);

  const stockStatus = useMemo(() => {
    if (product.stock === 0) return 'Out of Stock';
    if (product.stock < 10) return 'Low Stock';
    return 'In Stock';
  }, [product.stock]);

  return (
    <div className="product-card">
      <img 
        src={product.imageUrl} 
        alt={product.name}
        loading="lazy"
      />
      <div className="product-info">
        <h3>{product.name}</h3>
        <p className="description">{product.description}</p>
        <div className="price">{formattedPrice}</div>
        <div className={`stock ${stockStatus.toLowerCase().replace(' ', '-')}`}>
          {stockStatus}
        </div>
        <div className="actions">
          <button onClick={handleSelect} className="btn-primary">
            View Details
          </button>
          {canDelete && (
            <button onClick={handleDelete} className="btn-danger">
              Delete
            </button>
          )}
        </div>
      </div>
    </div>
  );
});

ProductCard.displayName = 'ProductCard';

// Lazy loading with React Suspense
const ProductManagement = lazy(() => 
  import('./ProductManagement').then(module => ({
    default: module.ProductManagement
  }))
);

const AnalyticsDashboard = lazy(() => 
  import('./AnalyticsDashboard').then(module => ({
    default: module.AnalyticsDashboard
  }))
);

export const App: React.FC = () => {
  return (
    <Router>
      <div className="app">
        <Suspense fallback={<LoadingSpinner />}>
          <Routes>
            <Route path="/products/*" element={<ProductManagement />} />
            <Route path="/analytics" element={<AnalyticsDashboard />} />
          </Routes>
        </Suspense>
      </div>
    </Router>
  );
};
  

9. Security Considerations

9.1 JWT Authentication & Authorization

  
    // Services/AuthService.cs
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.IdentityModel.Tokens;

namespace ECommerce.API.Services
{
    public interface IAuthService
    {
        Task<AuthResult> LoginAsync(LoginRequest request);
        Task<AuthResult> RegisterAsync(RegisterRequest request);
        Task<AuthResult> RefreshTokenAsync(string token, string refreshToken);
        Task<bool> RevokeTokenAsync(string userId);
    }

    public class AuthService : IAuthService
    {
        private readonly ApplicationDbContext _context;
        private readonly IConfiguration _configuration;
        private readonly ILogger<AuthService> _logger;

        public AuthService(
            ApplicationDbContext context,
            IConfiguration configuration,
            ILogger<AuthService> logger)
        {
            _context = context;
            _configuration = configuration;
            _logger = logger;
        }

        public async Task<AuthResult> LoginAsync(LoginRequest request)
        {
            try
            {
                var user = await _context.Users
                    .Include(u => u.Roles)
                    .FirstOrDefaultAsync(u => u.Email == request.Email);

                if (user == null || !VerifyPassword(request.Password, user.PasswordHash))
                {
                    return AuthResult.Failure("Invalid email or password");
                }

                var token = GenerateJwtToken(user);
                var refreshToken = GenerateRefreshToken();

                user.RefreshToken = refreshToken;
                user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(7);
                await _context.SaveChangesAsync();

                return AuthResult.Success(token, refreshToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during login for email: {Email}", request.Email);
                return AuthResult.Failure("An error occurred during login");
            }
        }

        public async Task<AuthResult> RegisterAsync(RegisterRequest request)
        {
            try
            {
                if (await _context.Users.AnyAsync(u => u.Email == request.Email))
                {
                    return AuthResult.Failure("Email already exists");
                }

                var user = new User
                {
                    Email = request.Email,
                    PasswordHash = HashPassword(request.Password),
                    FirstName = request.FirstName,
                    LastName = request.LastName,
                    CreatedAt = DateTime.UtcNow
                };

                // Assign default role
                var defaultRole = await _context.Roles.FirstOrDefaultAsync(r => r.Name == "User");
                if (defaultRole != null)
                {
                    user.Roles.Add(defaultRole);
                }

                _context.Users.Add(user);
                await _context.SaveChangesAsync();

                var token = GenerateJwtToken(user);
                var refreshToken = GenerateRefreshToken();

                user.RefreshToken = refreshToken;
                user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(7);
                await _context.SaveChangesAsync();

                return AuthResult.Success(token, refreshToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during registration for email: {Email}", request.Email);
                return AuthResult.Failure("An error occurred during registration");
            }
        }

        public async Task<AuthResult> RefreshTokenAsync(string token, string refreshToken)
        {
            try
            {
                var principal = GetPrincipalFromExpiredToken(token);
                var userId = principal.FindFirstValue(ClaimTypes.NameIdentifier);

                var user = await _context.Users
                    .Include(u => u.Roles)
                    .FirstOrDefaultAsync(u => u.Id == int.Parse(userId));

                if (user == null || user.RefreshToken != refreshToken || 
                    user.RefreshTokenExpiry <= DateTime.UtcNow)
                {
                    return AuthResult.Failure("Invalid refresh token");
                }

                var newToken = GenerateJwtToken(user);
                var newRefreshToken = GenerateRefreshToken();

                user.RefreshToken = newRefreshToken;
                user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(7);
                await _context.SaveChangesAsync();

                return AuthResult.Success(newToken, newRefreshToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during token refresh");
                return AuthResult.Failure("An error occurred during token refresh");
            }
        }

        public async Task<bool> RevokeTokenAsync(string userId)
        {
            try
            {
                var user = await _context.Users.FindAsync(int.Parse(userId));
                if (user == null) return false;

                user.RefreshToken = null;
                user.RefreshTokenExpiry = null;
                await _context.SaveChangesAsync();

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error revoking token for user: {UserId}", userId);
                return false;
            }
        }

        private string GenerateJwtToken(User user)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(_configuration["Jwt:Secret"]);

            var claims = new List<Claim>
            {
                new(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new(ClaimTypes.Email, user.Email),
                new(ClaimTypes.Name, $"{user.FirstName} {user.LastName}"),
                new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };

            // Add roles
            claims.AddRange(user.Roles.Select(role => 
                new Claim(ClaimTypes.Role, role.Name)));

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(claims),
                Expires = DateTime.UtcNow.AddMinutes(
                    double.Parse(_configuration["Jwt:ExpiryMinutes"] ?? "60")),
                Issuer = _configuration["Jwt:Issuer"],
                Audience = _configuration["Jwt:Audience"],
                SigningCredentials = new SigningCredentials(
                    new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(_configuration["Jwt:Secret"]);

            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateLifetime = false
            };

            var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out _);
            return principal;
        }

        private string GenerateRefreshToken()
        {
            var randomNumber = new byte[32];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

        private string HashPassword(string password)
        {
            return BCrypt.Net.BCrypt.HashPassword(password);
        }

        private bool VerifyPassword(string password, string passwordHash)
        {
            return BCrypt.Net.BCrypt.Verify(password, passwordHash);
        }
    }

    public class AuthResult
    {
        public bool Success { get; set; }
        public string? Token { get; set; }
        public string? RefreshToken { get; set; }
        public string? Error { get; set; }

        public static AuthResult Success(string token, string refreshToken)
        {
            return new AuthResult 
            { 
                Success = true, 
                Token = token, 
                RefreshToken = refreshToken 
            };
        }

        public static AuthResult Failure(string error)
        {
            return new AuthResult { Success = false, Error = error };
        }
    }
}
  

9.2 Frontend Security Implementation

  
    // React Security Hook
import { useState, useEffect, useCallback } from 'react';

interface User {
  id: number;
  email: string;
  firstName: string;
  lastName: string;
  roles: string[];
}

interface AuthState {
  user: User | null;
  isAuthenticated: boolean;
  isLoading: boolean;
  error: string | null;
}

export const useAuth = () => {
  const [authState, setAuthState] = useState<AuthState>({
    user: null,
    isAuthenticated: false,
    isLoading: true,
    error: null
  });

  const login = useCallback(async (email: string, password: string) => {
    try {
      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));
      
      const response = await apiClient.post<AuthResponse>('/api/auth/login', {
        email,
        password
      });

      const { token, refreshToken, user } = response;
      
      // Store tokens securely
      localStorage.setItem('authToken', token);
      localStorage.setItem('refreshToken', refreshToken);
      
      setAuthState({
        user,
        isAuthenticated: true,
        isLoading: false,
        error: null
      });
    } catch (error) {
      setAuthState(prev => ({
        ...prev,
        isLoading: false,
        error: error instanceof Error ? error.message : 'Login failed'
      }));
    }
  }, []);

  const logout = useCallback(() => {
    localStorage.removeItem('authToken');
    localStorage.removeItem('refreshToken');
    setAuthState({
      user: null,
      isAuthenticated: false,
      isLoading: false,
      error: null
    });
  }, []);

  const refreshToken = useCallback(async () => {
    try {
      const token = localStorage.getItem('authToken');
      const refreshToken = localStorage.getItem('refreshToken');
      
      if (!token || !refreshToken) {
        throw new Error('No tokens available');
      }

      const response = await apiClient.post<AuthResponse>('/api/auth/refresh', {
        token,
        refreshToken
      });

      localStorage.setItem('authToken', response.token);
      localStorage.setItem('refreshToken', response.refreshToken);
      
      setAuthState(prev => ({
        ...prev,
        user: response.user,
        isAuthenticated: true
      }));

      return response.token;
    } catch (error) {
      logout();
      throw error;
    }
  }, [logout]);

  useEffect(() => {
    const initializeAuth = async () => {
      const token = localStorage.getItem('authToken');
      
      if (!token) {
        setAuthState(prev => ({ ...prev, isLoading: false }));
        return;
      }

      try {
        // Verify token validity by fetching user profile
        const user = await apiClient.get<User>('/api/auth/profile');
        setAuthState({
          user,
          isAuthenticated: true,
          isLoading: false,
          error: null
        });
      } catch (error) {
        // Token is invalid, clear storage
        localStorage.removeItem('authToken');
        localStorage.removeItem('refreshToken');
        setAuthState({
          user: null,
          isAuthenticated: false,
          isLoading: false,
          error: null
        });
      }
    };

    initializeAuth();
  }, []);

  return {
    ...authState,
    login,
    logout,
    refreshToken
  };
};

// Protected Route Component
interface ProtectedRouteProps {
  children: React.ReactNode;
  requiredRoles?: string[];
}

export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ 
  children, 
  requiredRoles = [] 
}) => {
  const { isAuthenticated, user, isLoading } = useAuth();

  if (isLoading) {
    return <LoadingSpinner />;
  }

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  if (requiredRoles.length > 0 && user) {
    const hasRequiredRole = requiredRoles.some(role => 
      user.roles.includes(role)
    );
    
    if (!hasRequiredRole) {
      return <Navigate to="/unauthorized" replace />;
    }
  }

  return <>{children}</>;
};
  

10. Deployment Strategies

10.1 Docker Configuration

  
    # Backend Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["ECommerce.API/ECommerce.API.csproj", "ECommerce.API/"]
COPY ["ECommerce.Application/ECommerce.Application.csproj", "ECommerce.Application/"]
COPY ["ECommerce.Domain/ECommerce.Domain.csproj", "ECommerce.Domain/"]
COPY ["ECommerce.Infrastructure/ECommerce.Infrastructure.csproj", "ECommerce.Infrastructure/"]
COPY ["ECommerce.Shared/ECommerce.Shared.csproj", "ECommerce.Shared/"]
RUN dotnet restore "ECommerce.API/ECommerce.API.csproj"
COPY . .
WORKDIR "/src/ECommerce.API"
RUN dotnet build "ECommerce.API.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ECommerce.API.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ECommerce.API.dll"]

# Frontend Dockerfile (React example)
FROM node:18-alpine AS frontend-build
WORKDIR /app

# Copy package files
COPY package*.json ./
RUN npm ci --only=production

# Copy source code
COPY . .
RUN npm run build

# Production stage
FROM nginx:alpine
COPY --from=frontend-build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
  

10.2 Docker Compose for Full Stack

  
    # docker-compose.yml
version: '3.8'

services:
  # Database
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: ecommerce
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: securepassword
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - ecommerce-network

  # Redis Cache
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - ecommerce-network

  # Backend API
  api:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - ConnectionStrings__DefaultConnection=Host=postgres;Database=ecommerce;Username=admin;Password=securepassword
      - Redis__ConnectionString=redis:6379
      - Jwt__Secret=your-super-secret-key-at-least-32-characters-long
      - ASPNETCORE_ENVIRONMENT=Production
    ports:
      - "5000:80"
    depends_on:
      - postgres
      - redis
    networks:
      - ecommerce-network

  # React Frontend
  react-app:
    build:
      context: ./frontend/react-app
      dockerfile: Dockerfile
    environment:
      - VITE_API_BASE_URL=http://localhost:5000/api
    ports:
      - "3000:80"
    depends_on:
      - api
    networks:
      - ecommerce-network

  # Angular Frontend
  angular-app:
    build:
      context: ./frontend/angular-app
      dockerfile: Dockerfile
    environment:
      - API_BASE_URL=http://localhost:5000/api
    ports:
      - "4200:80"
    depends_on:
      - api
    networks:
      - ecommerce-network

  # Vue.js Frontend
  vue-app:
    build:
      context: ./frontend/vue-app
      dockerfile: Dockerfile
    environment:
      - VITE_API_BASE_URL=http://localhost:5000/api
    ports:
      - "8080:80"
    depends_on:
      - api
    networks:
      - ecommerce-network

  # Nginx Reverse Proxy
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - api
      - react-app
      - angular-app
      - vue-app
    networks:
      - ecommerce-network

volumes:
  postgres_data:

networks:
  ecommerce-network:
    driver: bridge
  

10.3 CI/CD Pipeline Configuration

  
    # .github/workflows/deploy.yml
name: Deploy ECommerce Application

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
    - uses: actions/checkout@v3
    
    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '8.0.x'
        
    - name: Restore dependencies
      run: dotnet restore
      
    - name: Run tests
      run: dotnet test --verbosity normal
      
    - name: Publish code coverage
      uses: codecov/codecov-action@v3

  build-backend:
    runs-on: ubuntu-latest
    needs: test
    steps:
    - uses: actions/checkout@v3
    
    - name: Build Docker image
      run: docker build -t ecommerce-api:${{ github.sha }} .
      
    - name: Log in to Docker Hub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}
        
    - name: Push Docker image
      run: |
        docker tag ecommerce-api:${{ github.sha }} ${{ secrets.DOCKER_USERNAME }}/ecommerce-api:latest
        docker push ${{ secrets.DOCKER_USERNAME }}/ecommerce-api:latest

  build-frontend:
    runs-on: ubuntu-latest
    needs: test
    strategy:
      matrix:
        frontend: [react, angular, vue]
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
        cache-dependency-path: frontend/${{ matrix.frontend }}-app/package-lock.json
        
    - name: Install dependencies
      run: npm ci
      working-directory: frontend/${{ matrix.frontend }}-app
      
    - name: Build application
      run: npm run build
      working-directory: frontend/${{ matrix.frontend }}-app
      env:
        VITE_API_BASE_URL: ${{ secrets.API_BASE_URL }}
        
    - name: Build Docker image
      run: docker build -t ecommerce-${{ matrix.frontend }}:${{ github.sha }} .
      working-directory: frontend/${{ matrix.frontend }}-app
      
    - name: Push Docker image
      run: |
        docker tag ecommerce-${{ matrix.frontend }}:${{ github.sha }} ${{ secrets.DOCKER_USERNAME }}/ecommerce-${{ matrix.frontend }}:latest
        docker push ${{ secrets.DOCKER_USERNAME }}/ecommerce-${{ matrix.frontend }}:latest

  deploy:
    runs-on: ubuntu-latest
    needs: [build-backend, build-frontend]
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Deploy to production
      uses: appleboy/[email protected]
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USERNAME }}
        key: ${{ secrets.SERVER_SSH_KEY }}
        script: |
          cd /opt/ecommerce
          docker-compose pull
          docker-compose up -d
          docker system prune -f
  

This comprehensive guide covers the complete integration of JavaScript frameworks with  ASP.NET  Core, providing real-world examples, best practices, and production-ready code patterns. The modular approach allows you to choose the integration strategy that best fits your project requirements while maintaining scalability, performance, and security.