dramaling-vocab-learning/docs/AI驅動產品後端技術架構指南.md

50 KiB
Raw Blame History

AI驅動產品後端技術架構指南

📋 文件資訊

  • 文件名稱: AI驅動產品後端技術架構指南
  • 版本: v1.0
  • 建立日期: 2025-01-25
  • 最後更新: 2025-01-25
  • 負責團隊: DramaLing技術架構團隊
  • 適用產品: AI驅動的語言學習、內容分析、智能推薦等產品

🎯 架構設計原則

核心設計理念

高效率 (High Performance)

響應速度:
  - API響應時間 < 500ms (不含AI處理)
  - AI處理時間 < 5秒
  - 資料庫查詢 < 100ms
  - 快取命中率 > 80%

並發處理:
  - 支援1000+併發請求
  - 優雅降級機制
  - 資源池化管理
  - 非同步處理優先

好維護 (Maintainability)

程式碼品質:
  - 單一職責原則 (SRP)
  - 依賴注入 (DI)
  - 介面隔離 (ISP)
  - 開放封閉原則 (OCP)

文檔完整性:
  - API文檔自動生成
  - 程式碼註釋覆蓋率 > 60%
  - 架構決策記錄 (ADR)
  - 部署流程文檔化

穩定性 (Stability)

錯誤處理:
  - 全局異常處理
  - 優雅降級策略
  - 重試機制設計
  - 斷路器模式

監控告警:
  - 健康檢查端點
  - 關鍵指標監控
  - 自動告警機制
  - 日誌追蹤完整

🏗️ 分層架構設計

整體架構圖

┌─────────────────────────────────────────────────────────────┐
│                        🌐 API Gateway                        │
│                    (Authentication, Rate Limiting)           │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                   📡 Controllers Layer                       │
│              (Request Handling, Validation)                  │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                   🔧 Services Layer                         │
│              (Business Logic, AI Integration)                │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                   💾 Data Access Layer                      │
│              (Repository Pattern, EF Core)                   │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                   🗄️ Data Storage Layer                     │
│            (Database, Cache, External APIs)                  │
└─────────────────────────────────────────────────────────────┘

分層職責定義

1. Controllers Layer (控制器層)

職責: 處理HTTP請求、參數驗證、回應格式化

[ApiController]
[Route("api/[controller]")]
public class AIController : ControllerBase
{
    private readonly IAIAnalysisService _aiService;
    private readonly ILogger<AIController> _logger;

    // ✅ 好的實踐單一職責只處理HTTP層邏輯
    [HttpPost("analyze")]
    public async Task<ActionResult<AnalysisResponse>> Analyze(
        [FromBody] AnalysisRequest request)
    {
        // 1. 輸入驗證
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        // 2. 委派給服務層
        var result = await _aiService.AnalyzeAsync(request);

        // 3. 格式化回應
        return Ok(result);
    }
}

2. Services Layer (服務層)

職責: 業務邏輯實現、AI服務整合、快取策略

public interface IAIAnalysisService
{
    Task<AnalysisResult> AnalyzeAsync(AnalysisRequest request);
}

public class AIAnalysisService : IAIAnalysisService
{
    private readonly IGeminiService _geminiService;
    private readonly ICacheService _cacheService;
    private readonly IAnalysisRepository _repository;

    // ✅ 好的實踐:依賴注入,介面隔離
    public async Task<AnalysisResult> AnalyzeAsync(AnalysisRequest request)
    {
        // 1. 快取檢查
        var cached = await _cacheService.GetAsync(request.InputText);
        if (cached != null) return cached;

        // 2. AI服務調用
        var aiResult = await _geminiService.AnalyzeAsync(request.InputText);

        // 3. 業務邏輯處理
        var processedResult = ProcessAIResult(aiResult, request);

        // 4. 快取存儲
        await _cacheService.SetAsync(request.InputText, processedResult);

        // 5. 持久化
        await _repository.SaveAnalysisAsync(processedResult);

        return processedResult;
    }
}

3. Data Access Layer (資料存取層)

職責: 資料庫操作、查詢優化、事務管理

public interface IAnalysisRepository
{
    Task<AnalysisResult?> GetCachedAnalysisAsync(string inputHash);
    Task SaveAnalysisAsync(AnalysisResult result);
    Task<IEnumerable<AnalysisStats>> GetUsageStatsAsync(DateTime from, DateTime to);
}

public class AnalysisRepository : IAnalysisRepository
{
    private readonly DramaLingDbContext _context;

    // ✅ 好的實踐Repository模式查詢優化
    public async Task<AnalysisResult?> GetCachedAnalysisAsync(string inputHash)
    {
        return await _context.AnalysisCache
            .AsNoTracking()  // 性能優化:只讀查詢
            .Where(a => a.InputHash == inputHash && a.ExpiresAt > DateTime.UtcNow)
            .Select(a => new AnalysisResult  // 投影查詢:只選需要的欄位
            {
                Data = a.CachedData,
                CreatedAt = a.CreatedAt
            })
            .FirstOrDefaultAsync();
    }
}

🤖 AI整合架構模式

AI服務抽象層設計

多AI提供商支援架構

// ✅ 策略模式支援多個AI提供商
public interface IAIProvider
{
    string ProviderName { get; }
    Task<AIResponse> AnalyzeAsync(AIRequest request);
    bool IsAvailable { get; }
    decimal CostPerRequest { get; }
}

public class GeminiProvider : IAIProvider
{
    public string ProviderName => "Google Gemini";

    public async Task<AIResponse> AnalyzeAsync(AIRequest request)
    {
        // Gemini特定實現
    }
}

public class OpenAIProvider : IAIProvider
{
    public string ProviderName => "OpenAI GPT";

    public async Task<AIResponse> AnalyzeAsync(AIRequest request)
    {
        // OpenAI特定實現
    }
}

// AI提供商選擇器
public class AIProviderSelector
{
    private readonly IEnumerable<IAIProvider> _providers;

    public IAIProvider SelectBestProvider(AIRequest request)
    {
        // 基於成本、可用性、性能選擇最佳提供商
        return _providers
            .Where(p => p.IsAvailable)
            .OrderBy(p => p.CostPerRequest)
            .First();
    }
}

AI請求優化模式

智能批次處理

public class BatchAIProcessor
{
    private readonly Queue<AIRequest> _requestQueue = new();
    private readonly Timer _batchTimer;

    public async Task<AIResponse> ProcessAsync(AIRequest request)
    {
        // ✅ 批次處理提高AI API效率
        _requestQueue.Enqueue(request);

        if (_requestQueue.Count >= BatchSize || IsTimeoutReached())
        {
            await ProcessBatchAsync();
        }

        return await GetResultAsync(request.Id);
    }

    private async Task ProcessBatchAsync()
    {
        var batch = DrainQueue();
        var batchPrompt = CombineRequests(batch);
        var response = await _aiProvider.AnalyzeAsync(batchPrompt);
        var results = SplitResponse(response, batch);

        // 分發結果給等待的請求
        foreach (var result in results)
        {
            _resultStore.SetResult(result.RequestId, result);
        }
    }
}

智能快取策略

public class IntelligentCacheService
{
    private readonly IMemoryCache _memoryCache;
    private readonly IDistributedCache _distributedCache;
    private readonly IDatabase _database;

    // ✅ 多層快取:記憶體 → Redis → 資料庫
    public async Task<T?> GetAsync<T>(string key) where T : class
    {
        // L1: 記憶體快取 (最快)
        if (_memoryCache.TryGetValue(key, out T? memoryResult))
            return memoryResult;

        // L2: 分散式快取 (Redis)
        var distributedResult = await _distributedCache.GetStringAsync(key);
        if (distributedResult != null)
        {
            var result = JsonSerializer.Deserialize<T>(distributedResult);
            _memoryCache.Set(key, result, TimeSpan.FromMinutes(5));
            return result;
        }

        // L3: 資料庫快取 (持久化)
        var dbResult = await GetFromDatabaseAsync<T>(key);
        if (dbResult != null)
        {
            await SetMultiLevelCacheAsync(key, dbResult);
            return dbResult;
        }

        return null;
    }

    // 智能過期策略
    public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
    {
        var smartExpiry = CalculateSmartExpiry(key, value);

        // 同時更新多層快取
        _memoryCache.Set(key, value, smartExpiry);
        await _distributedCache.SetStringAsync(key, JsonSerializer.Serialize(value),
            new DistributedCacheEntryOptions { SlidingExpiration = smartExpiry });
        await SaveToDatabaseAsync(key, value, smartExpiry);
    }
}

🔧 程式碼組織結構

專案結構範本

DramaLing.Api/
├── 📁 Controllers/          # API控制器
│   ├── AIController.cs      # AI分析相關端點
│   ├── AuthController.cs    # 認證相關端點
│   └── BaseController.cs    # 共用控制器基類
├── 📁 Services/             # 業務服務層
│   ├── AI/                  # AI相關服務
│   │   ├── IGeminiService.cs
│   │   ├── GeminiService.cs
│   │   ├── IPromptBuilder.cs
│   │   └── PromptBuilder.cs
│   ├── Cache/               # 快取服務
│   │   ├── ICacheService.cs
│   │   ├── MemoryCacheService.cs
│   │   └── RedisCacheService.cs
│   └── Core/                # 核心業務服務
│       ├── IAnalysisService.cs
│       └── AnalysisService.cs
├── 📁 Models/               # 資料模型
│   ├── Entities/            # 資料庫實體
│   ├── DTOs/               # 資料傳輸對象
│   └── Requests/           # API請求模型
├── 📁 Data/                # 資料存取層
│   ├── DbContext.cs        # EF Core上下文
│   ├── Repositories/       # Repository實現
│   └── Migrations/         # 資料庫遷移
├── 📁 Infrastructure/      # 基礎設施
│   ├── Middleware/         # 中介軟體
│   ├── Filters/           # 過濾器
│   ├── Extensions/        # 擴展方法
│   └── Configuration/     # 配置管理
├── 📁 Utils/              # 工具類
│   ├── Helpers/           # 輔助函數
│   ├── Constants/         # 常數定義
│   └── Validators/        # 驗證器
└── Program.cs             # 應用程式入口

依賴注入最佳實踐

服務註冊配置

// Program.cs
public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);

    // ✅ 分層註冊:按類型組織
    RegisterControllers(builder);
    RegisterBusinessServices(builder);
    RegisterDataServices(builder);
    RegisterInfrastructureServices(builder);
    RegisterAIServices(builder);

    var app = builder.Build();
    ConfigureMiddleware(app);
    app.Run();
}

// 業務服務註冊
private static void RegisterBusinessServices(WebApplicationBuilder builder)
{
    builder.Services.AddScoped<IAnalysisService, AnalysisService>();
    builder.Services.AddScoped<IUserService, UserService>();
    builder.Services.AddScoped<IFlashcardService, FlashcardService>();
}

// AI服務註冊
private static void RegisterAIServices(WebApplicationBuilder builder)
{
    // ✅ 工廠模式支援多AI提供商
    builder.Services.AddSingleton<IAIProviderFactory, AIProviderFactory>();
    builder.Services.AddScoped<IGeminiService, GeminiService>();
    builder.Services.AddScoped<IPromptBuilder, PromptBuilder>();

    // ✅ 配置模式:強型別配置
    builder.Services.Configure<GeminiOptions>(
        builder.Configuration.GetSection("Gemini"));

    // ✅ HttpClient工廠連接池管理
    builder.Services.AddHttpClient<IGeminiService, GeminiService>(client =>
    {
        client.Timeout = TimeSpan.FromSeconds(30);
        client.DefaultRequestHeaders.Add("User-Agent", "DramaLing/1.0");
    });
}

🚀 性能優化策略

資料庫性能優化

Entity Framework最佳實踐

public class OptimizedAnalysisRepository : IAnalysisRepository
{
    private readonly DramaLingDbContext _context;

    // ✅ 查詢優化AsNoTracking + 投影
    public async Task<IEnumerable<AnalysisDto>> GetRecentAnalysesAsync(int userId, int count)
    {
        return await _context.Analyses
            .AsNoTracking()  // 關閉變更追蹤:提升查詢性能
            .Where(a => a.UserId == userId)
            .OrderByDescending(a => a.CreatedAt)
            .Take(count)
            .Select(a => new AnalysisDto  // 投影查詢:只查詢需要的欄位
            {
                Id = a.Id,
                InputText = a.InputText,
                CreatedAt = a.CreatedAt,
                Summary = a.Summary
            })
            .ToListAsync();
    }

    // ✅ 批次操作:減少資料庫往返
    public async Task SaveMultipleAnalysesAsync(IEnumerable<Analysis> analyses)
    {
        _context.Analyses.AddRange(analyses);  // 批次新增
        await _context.SaveChangesAsync();    // 單次提交
    }

    // ✅ 連接分離:讀寫分離
    public async Task<AnalysisStats> GetAnalysisStatsAsync(int userId)
    {
        using var readOnlyContext = CreateReadOnlyContext();
        return await readOnlyContext.Analyses
            .Where(a => a.UserId == userId)
            .GroupBy(a => a.UserId)
            .Select(g => new AnalysisStats
            {
                TotalCount = g.Count(),
                LastAnalysisDate = g.Max(a => a.CreatedAt)
            })
            .FirstOrDefaultAsync();
    }
}

AI服務性能優化

請求合併與批次處理

public class OptimizedAIService : IAIAnalysisService
{
    private readonly IBatchProcessor<AIRequest, AIResponse> _batchProcessor;
    private readonly ICircuitBreaker _circuitBreaker;

    // ✅ 請求合併減少AI API調用次數
    public async Task<AnalysisResult> AnalyzeAsync(AnalysisRequest request)
    {
        var aiRequest = new AIRequest
        {
            Id = Guid.NewGuid(),
            InputText = request.InputText,
            Timestamp = DateTime.UtcNow
        };

        // 批次處理:自動合併相近時間的請求
        var aiResponse = await _batchProcessor.ProcessAsync(aiRequest);

        return TransformToAnalysisResult(aiResponse);
    }

    // ✅ 斷路器模式防止AI服務故障影響整體系統
    public async Task<AIResponse> CallAIWithCircuitBreakerAsync(AIRequest request)
    {
        return await _circuitBreaker.ExecuteAsync(async () =>
        {
            var response = await _aiProvider.CallAsync(request);

            if (!response.IsSuccessful)
                throw new AIServiceException(response.ErrorMessage);

            return response;
        });
    }
}

快取優化策略

多層快取架構

public class HybridCacheService : ICacheService
{
    private readonly IMemoryCache _l1Cache;      // L1: 程序內快取
    private readonly IDistributedCache _l2Cache; // L2: Redis分散式快取
    private readonly ICacheRepository _l3Cache;  // L3: 資料庫快取

    // ✅ 智能快取:根據數據特性選擇策略
    public async Task<T?> GetAsync<T>(string key) where T : class
    {
        var cacheStrategy = DetermineCacheStrategy<T>();

        return cacheStrategy switch
        {
            CacheStrategy.Fast => await GetFromL1Async<T>(key),
            CacheStrategy.Distributed => await GetFromL2Async<T>(key),
            CacheStrategy.Persistent => await GetFromL3Async<T>(key),
            _ => await GetFromMultiLevelAsync<T>(key)
        };
    }

    // 智能過期策略
    private TimeSpan CalculateExpiry(string key, object value)
    {
        return key switch
        {
            var k when k.StartsWith("analysis:") => TimeSpan.FromHours(24),
            var k when k.StartsWith("user:") => TimeSpan.FromMinutes(30),
            var k when k.StartsWith("stats:") => TimeSpan.FromMinutes(5),
            _ => TimeSpan.FromMinutes(15)
        };
    }

    // ✅ 快取預熱:提前載入熱點資料
    public async Task WarmupCacheAsync()
    {
        var hotData = await _repository.GetHotDataAsync();
        var tasks = hotData.Select(data =>
            SetAsync($"warmup:{data.Id}", data, TimeSpan.FromHours(1)));

        await Task.WhenAll(tasks);
    }
}

🛡️ 錯誤處理與穩定性

全局異常處理中介軟體

public class GlobalExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionHandlingMiddleware> _logger;

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    // ✅ 結構化錯誤處理:不同異常類型對應不同處理策略
    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var response = exception switch
        {
            ValidationException validationEx => CreateValidationErrorResponse(validationEx),
            AIServiceException aiEx => CreateAIServiceErrorResponse(aiEx),
            DatabaseException dbEx => CreateDatabaseErrorResponse(dbEx),
            UnauthorizedAccessException authEx => CreateUnauthorizedResponse(authEx),
            _ => CreateGenericErrorResponse(exception)
        };

        // 記錄錯誤日誌
        _logger.LogError(exception, "Request failed: {Method} {Path}",
            context.Request.Method, context.Request.Path);

        // 返回結構化錯誤回應
        context.Response.StatusCode = response.StatusCode;
        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(JsonSerializer.Serialize(response));
    }
}

重試與斷路器模式

public class ResilientAIService : IAIAnalysisService
{
    private readonly IAIProvider _primaryProvider;
    private readonly IAIProvider _fallbackProvider;
    private readonly ICircuitBreaker _circuitBreaker;

    // ✅ 重試機制:指數退避
    public async Task<AIResponse> CallWithRetryAsync(AIRequest request)
    {
        var retryPolicy = Policy
            .Handle<AIServiceException>()
            .Or<HttpRequestException>()
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt =>
                    TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), // 指數退避2^n秒
                onRetry: (outcome, timespan, retryCount, context) =>
                {
                    _logger.LogWarning("AI service retry {RetryCount} after {Delay}ms",
                        retryCount, timespan.TotalMilliseconds);
                });

        return await retryPolicy.ExecuteAsync(async () =>
        {
            return await _circuitBreaker.ExecuteAsync(async () =>
            {
                try
                {
                    return await _primaryProvider.AnalyzeAsync(request);
                }
                catch (AIServiceException)
                {
                    // ✅ 降級策略:使用備用提供商
                    _logger.LogWarning("Primary AI provider failed, using fallback");
                    return await _fallbackProvider.AnalyzeAsync(request);
                }
            });
        });
    }
}

📊 監控與可觀測性

結構化日誌設計

日誌標準化

public static class LoggerExtensions
{
    // ✅ 結構化日誌:便於查詢和分析
    public static void LogAIRequest(this ILogger logger, string requestId,
        string inputText, string provider, double processingTime)
    {
        logger.LogInformation("AI Request Completed: {RequestId} Provider: {Provider} " +
            "InputLength: {InputLength} ProcessingTime: {ProcessingTime}ms",
            requestId, provider, inputText.Length, processingTime);
    }

    public static void LogBusinessOperation(this ILogger logger, string operation,
        string userId, bool success, Dictionary<string, object>? additionalData = null)
    {
        using var scope = logger.BeginScope(new Dictionary<string, object>
        {
            ["Operation"] = operation,
            ["UserId"] = userId,
            ["Success"] = success,
            ["Timestamp"] = DateTime.UtcNow,
            ["AdditionalData"] = additionalData ?? new Dictionary<string, object>()
        });

        if (success)
            logger.LogInformation("Business operation completed successfully");
        else
            logger.LogWarning("Business operation failed");
    }
}

健康檢查系統

public class AIServiceHealthCheck : IHealthCheck
{
    private readonly IGeminiService _geminiService;
    private readonly ICacheService _cacheService;
    private readonly DramaLingDbContext _dbContext;

    // ✅ 全面健康檢查AI服務、快取、資料庫
    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        var checks = new List<(string Name, Task<bool> Check)>
        {
            ("Gemini API", CheckGeminiHealthAsync()),
            ("Cache Service", CheckCacheHealthAsync()),
            ("Database", CheckDatabaseHealthAsync()),
            ("Memory Usage", CheckMemoryUsageAsync())
        };

        var results = await Task.WhenAll(checks.Select(async check =>
        {
            try
            {
                var isHealthy = await check.Check;
                return (check.Name, IsHealthy: isHealthy, Error: (string?)null);
            }
            catch (Exception ex)
            {
                return (check.Name, IsHealthy: false, Error: ex.Message);
            }
        }));

        var failedChecks = results.Where(r => !r.IsHealthy).ToList();

        if (failedChecks.Any())
        {
            var errors = string.Join(", ", failedChecks.Select(f => $"{f.Name}: {f.Error}"));
            return HealthCheckResult.Unhealthy($"Failed checks: {errors}");
        }

        return HealthCheckResult.Healthy("All systems operational");
    }
}

🔒 安全架構設計

多層安全防護

API安全中介軟體

public class SecurityMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<SecurityMiddleware> _logger;

    public async Task InvokeAsync(HttpContext context)
    {
        // ✅ 輸入驗證:防止注入攻擊
        if (!await ValidateInputSafetyAsync(context))
        {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsync("Invalid input detected");
            return;
        }

        // ✅ 速率限制:防止濫用
        if (!await CheckRateLimitAsync(context))
        {
            context.Response.StatusCode = 429;
            await context.Response.WriteAsync("Rate limit exceeded");
            return;
        }

        // ✅ 請求追蹤:安全稽核
        var requestId = Guid.NewGuid().ToString();
        context.Items["RequestId"] = requestId;

        using var scope = _logger.BeginScope(new Dictionary<string, object>
        {
            ["RequestId"] = requestId,
            ["UserId"] = context.User?.Identity?.Name ?? "Anonymous",
            ["IPAddress"] = context.Connection.RemoteIpAddress?.ToString(),
            ["UserAgent"] = context.Request.Headers["User-Agent"].ToString()
        });

        await _next(context);
    }

    private async Task<bool> ValidateInputSafetyAsync(HttpContext context)
    {
        // 檢查惡意模式
        var suspiciousPatterns = new[]
        {
            @"<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>",  // XSS
            @"(\bUNION\b|\bSELECT\b|\bINSERT\b|\bDELETE\b)",      // SQL注入
            @"(javascript:|data:|vbscript:)",                       // 協議注入
        };

        var body = await ReadRequestBodyAsync(context);
        return !suspiciousPatterns.Any(pattern =>
            Regex.IsMatch(body, pattern, RegexOptions.IgnoreCase));
    }
}

API金鑰管理

安全配置管理

public class SecureConfigurationService
{
    private readonly IConfiguration _configuration;
    private readonly IKeyVault _keyVault;  // Azure Key Vault 或 AWS Secrets Manager

    // ✅ 多層金鑰管理:環境變數 > Key Vault > 配置檔案
    public async Task<string> GetAPIKeyAsync(string serviceName)
    {
        // 優先級1: 環境變數(容器化部署)
        var envKey = Environment.GetEnvironmentVariable($"{serviceName.ToUpper()}_API_KEY");
        if (!string.IsNullOrEmpty(envKey))
            return envKey;

        // 優先級2: Key Vault生產環境
        if (_keyVault != null)
        {
            var vaultKey = await _keyVault.GetSecretAsync($"{serviceName}-api-key");
            if (!string.IsNullOrEmpty(vaultKey))
                return vaultKey;
        }

        // 優先級3: 配置檔案(開發環境)
        var configKey = _configuration[$"ApiKeys:{serviceName}"];
        if (!string.IsNullOrEmpty(configKey))
            return configKey;

        throw new InvalidOperationException($"API key for {serviceName} not found");
    }

    // ✅ 金鑰輪換定期更新API金鑰
    public async Task RotateAPIKeyAsync(string serviceName)
    {
        var newKey = await GenerateNewKeyAsync(serviceName);
        await _keyVault.SetSecretAsync($"{serviceName}-api-key", newKey);

        // 通知相關服務更新金鑰
        await NotifyKeyRotationAsync(serviceName, newKey);
    }
}

📈 可擴展性設計

微服務準備架構

服務邊界定義

// ✅ 領域驅動設計:按業務領域劃分服務
namespace DramaLing.AI.Domain
{
    // AI分析聚合根
    public class AnalysisAggregate
    {
        public AnalysisId Id { get; private set; }
        public string InputText { get; private set; }
        public AnalysisResult Result { get; private set; }
        public AnalysisStatus Status { get; private set; }

        // 業務邏輯封裝在聚合內
        public void MarkAsCompleted(AnalysisResult result)
        {
            if (Status != AnalysisStatus.Processing)
                throw new InvalidOperationException("Analysis is not in processing state");

            Result = result;
            Status = AnalysisStatus.Completed;

            // 發布領域事件
            AddDomainEvent(new AnalysisCompletedEvent(Id, result));
        }
    }
}

// 服務介面定義
public interface IAIAnalysisDomainService
{
    Task<AnalysisAggregate> StartAnalysisAsync(string inputText);
    Task CompleteAnalysisAsync(AnalysisId id, AnalysisResult result);
}

事件驅動架構

public class EventDrivenAIService
{
    private readonly IEventBus _eventBus;
    private readonly IAIProvider _aiProvider;

    // ✅ 事件驅動:解耦業務流程
    public async Task<AnalysisResult> ProcessAnalysisAsync(AnalysisRequest request)
    {
        // 1. 發布分析開始事件
        await _eventBus.PublishAsync(new AnalysisStartedEvent
        {
            RequestId = request.Id,
            InputText = request.InputText,
            Timestamp = DateTime.UtcNow
        });

        try
        {
            // 2. 執行AI分析
            var result = await _aiProvider.AnalyzeAsync(request);

            // 3. 發布分析完成事件
            await _eventBus.PublishAsync(new AnalysisCompletedEvent
            {
                RequestId = request.Id,
                Result = result,
                ProcessingTime = result.ProcessingTime
            });

            return result;
        }
        catch (Exception ex)
        {
            // 4. 發布分析失敗事件
            await _eventBus.PublishAsync(new AnalysisFailedEvent
            {
                RequestId = request.Id,
                Error = ex.Message,
                Timestamp = DateTime.UtcNow
            });

            throw;
        }
    }
}

// 事件處理器
public class AnalysisEventHandler :
    IEventHandler<AnalysisCompletedEvent>,
    IEventHandler<AnalysisFailedEvent>
{
    public async Task HandleAsync(AnalysisCompletedEvent eventData)
    {
        // 更新統計資料、發送通知、清理資源等
        await UpdateAnalysisStatsAsync(eventData);
        await NotifyUserAsync(eventData.RequestId, "分析完成");
    }

    public async Task HandleAsync(AnalysisFailedEvent eventData)
    {
        // 錯誤恢復、告警、重試邏輯等
        await LogFailureAsync(eventData);
        await TriggerRetryIfNecessaryAsync(eventData);
    }
}

🔧 配置管理最佳實踐

強型別配置

// ✅ 配置類別:型別安全的配置管理
public class AIServiceOptions
{
    public const string SectionName = "AIService";

    [Required]
    public string GeminiApiKey { get; set; } = string.Empty;

    [Range(1, 300)]
    public int MaxInputLength { get; set; } = 300;

    [Range(1, 60)]
    public int TimeoutSeconds { get; set; } = 30;

    public RetryOptions Retry { get; set; } = new();
    public CacheOptions Cache { get; set; } = new();
}

public class RetryOptions
{
    public int MaxAttempts { get; set; } = 3;
    public int BaseDelayMs { get; set; } = 1000;
    public bool UseExponentialBackoff { get; set; } = true;
}

// 配置驗證
public class AIServiceOptionsValidator : IValidateOptions<AIServiceOptions>
{
    public ValidateOptionsResult Validate(string name, AIServiceOptions options)
    {
        var failures = new List<string>();

        if (string.IsNullOrWhiteSpace(options.GeminiApiKey))
            failures.Add("Gemini API key is required");

        if (options.TimeoutSeconds <= 0)
            failures.Add("Timeout must be positive");

        return failures.Any()
            ? ValidateOptionsResult.Fail(failures)
            : ValidateOptionsResult.Success;
    }
}

// 註冊配置
builder.Services.Configure<AIServiceOptions>(
    builder.Configuration.GetSection(AIServiceOptions.SectionName));
builder.Services.AddSingleton<IValidateOptions<AIServiceOptions>, AIServiceOptionsValidator>();

環境特定配置

// appsettings.Development.json
{
  "AIService": {
    "GeminiApiKey": "dev-key",
    "TimeoutSeconds": 10,
    "Cache": {
      "EnableDistributed": false,
      "DefaultExpiry": "00:05:00"
    }
  },
  "Logging": {
    "LogLevel": {
      "DramaLing.AI": "Debug"
    }
  }
}

// appsettings.Production.json
{
  "AIService": {
    "TimeoutSeconds": 30,
    "Cache": {
      "EnableDistributed": true,
      "DefaultExpiry": "01:00:00"
    }
  },
  "Logging": {
    "LogLevel": {
      "DramaLing.AI": "Information"
    }
  }
}

🧪 測試架構設計

測試金字塔實作

單元測試 (70%)

public class AIAnalysisServiceTests
{
    private readonly Mock<IGeminiService> _mockGeminiService;
    private readonly Mock<ICacheService> _mockCacheService;
    private readonly AIAnalysisService _service;

    // ✅ 純邏輯測試:快速、可靠、獨立
    [Test]
    public async Task AnalyzeAsync_WithCachedResult_ReturnsCachedData()
    {
        // Arrange
        var request = new AnalysisRequest { InputText = "test sentence" };
        var cachedResult = new AnalysisResult { /* ... */ };

        _mockCacheService.Setup(c => c.GetAsync<AnalysisResult>(It.IsAny<string>()))
            .ReturnsAsync(cachedResult);

        // Act
        var result = await _service.AnalyzeAsync(request);

        // Assert
        Assert.That(result, Is.EqualTo(cachedResult));
        _mockGeminiService.Verify(g => g.AnalyzeAsync(It.IsAny<string>()), Times.Never);
    }

    // ✅ 錯誤情境測試
    [Test]
    public async Task AnalyzeAsync_WhenAIServiceFails_ThrowsAIServiceException()
    {
        // Arrange
        _mockCacheService.Setup(c => c.GetAsync<AnalysisResult>(It.IsAny<string>()))
            .ReturnsAsync((AnalysisResult?)null);
        _mockGeminiService.Setup(g => g.AnalyzeAsync(It.IsAny<string>()))
            .ThrowsAsync(new HttpRequestException("API unavailable"));

        var request = new AnalysisRequest { InputText = "test sentence" };

        // Act & Assert
        Assert.ThrowsAsync<AIServiceException>(() => _service.AnalyzeAsync(request));
    }
}

整合測試 (20%)

public class AIControllerIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    private readonly HttpClient _client;

    // ✅ 真實環境測試:包含所有中介軟體和配置
    [Test]
    public async Task AnalyzeSentence_WithValidInput_ReturnsSuccessResponse()
    {
        // Arrange
        var request = new AnalysisRequest
        {
            InputText = "She just join the team",
            AnalysisMode = "full"
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/ai/analyze-sentence", request);

        // Assert
        response.EnsureSuccessStatusCode();
        var result = await response.Content.ReadFromJsonAsync<AnalysisResponse>();

        Assert.That(result.Success, Is.True);
        Assert.That(result.Data, Is.Not.Null);
        Assert.That(result.Data.VocabularyAnalysis, Is.Not.Empty);
    }
}

E2E測試 (10%)

public class AIAnalysisE2ETests
{
    private readonly TestServer _server;
    private readonly HttpClient _client;

    // ✅ 端到端測試包含真實的AI API調用
    [Test]
    [Category("E2E")]
    public async Task CompleteAnalysisWorkflow_WithRealAI_ProducesValidResults()
    {
        // 此測試使用真實的AI API運行時間較長
        // 適合在CI/CD流程中定期執行

        var testSentences = new[]
        {
            "She just join the team, so let's cut her some slack.",
            "The implementation was challenging but rewarding.",
            "Could you please review the documentation?"
        };

        foreach (var sentence in testSentences)
        {
            var request = new AnalysisRequest { InputText = sentence };
            var response = await _client.PostAsJsonAsync("/api/ai/analyze-sentence", request);

            response.EnsureSuccessStatusCode();
            var result = await response.Content.ReadFromJsonAsync<AnalysisResponse>();

            // 驗證AI分析品質
            Assert.That(result.Data.VocabularyAnalysis.Count, Is.GreaterThan(0));
            Assert.That(result.Data.SentenceMeaning, Is.Not.Empty);

            // 驗證CEFR等級分配合理性
            Assert.That(result.Data.VocabularyAnalysis.Values
                .All(v => Enum.IsDefined(typeof(CEFRLevel), v.DifficultyLevel)), Is.True);
        }
    }
}

🔄 部署與DevOps架構

容器化配置

Dockerfile最佳實踐

# ✅ 多階段建置:減小鏡像大小
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# ✅ 快取優化:先複製專案檔案
COPY ["DramaLing.Api/DramaLing.Api.csproj", "DramaLing.Api/"]
COPY ["DramaLing.Core/DramaLing.Core.csproj", "DramaLing.Core/"]
RUN dotnet restore "DramaLing.Api/DramaLing.Api.csproj"

# 複製原始碼並建置
COPY . .
WORKDIR "/src/DramaLing.Api"
RUN dotnet build "DramaLing.Api.csproj" -c Release -o /app/build

# 發布應用程式
FROM build AS publish
RUN dotnet publish "DramaLing.Api.csproj" -c Release -o /app/publish --no-restore

# ✅ 執行階段:使用最小鏡像
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app

# ✅ 安全設定非root用戶
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser

COPY --from=publish /app/publish .

# ✅ 健康檢查:容器監控
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:5000/health || exit 1

ENTRYPOINT ["dotnet", "DramaLing.Api.dll"]

Kubernetes部署配置

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dramaling-api
spec:
  replicas: 3  # ✅ 高可用:多實例部署
  strategy:
    type: RollingUpdate  # ✅ 零停機部署
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    spec:
      containers:
      - name: api
        image: dramaling/api:latest
        resources:  # ✅ 資源限制:防止資源爭用
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        env:  # ✅ 外部化配置
        - name: ASPNETCORE_ENVIRONMENT
          value: "Production"
        - name: GEMINI_API_KEY
          valueFrom:
            secretKeyRef:
              name: ai-api-keys
              key: gemini-key
        livenessProbe:  # ✅ 自動恢復
          httpGet:
            path: /health
            port: 5000
          initialDelaySeconds: 30
          periodSeconds: 30
        readinessProbe:  # ✅ 流量控制
          httpGet:
            path: /health/ready
            port: 5000
          initialDelaySeconds: 5
          periodSeconds: 5

📊 監控指標設計

關鍵性能指標 (KPIs)

業務指標監控

public class BusinessMetricsService
{
    private readonly IMetricsCollector _metrics;

    // ✅ 業務指標:追蹤產品健康度
    public void RecordAnalysisMetrics(AnalysisResult result)
    {
        // AI分析成功率
        _metrics.Increment("ai.analysis.completed", new[]
        {
            new KeyValuePair<string, object>("provider", result.Provider),
            new KeyValuePair<string, object>("processing_time", result.ProcessingTime)
        });

        // 詞彙複雜度分布
        _metrics.Histogram("vocabulary.difficulty.distribution",
            result.VocabularyAnalysis.Values.Select(v => GetDifficultyScore(v.DifficultyLevel)));

        // 用戶參與度
        _metrics.Increment("user.engagement", new[]
        {
            new KeyValuePair<string, object>("feature", "ai_analysis"),
            new KeyValuePair<string, object>("session_id", result.SessionId)
        });
    }

    // ✅ 異常指標:快速發現問題
    public void RecordErrorMetrics(Exception exception, string operation)
    {
        _metrics.Increment("errors.total", new[]
        {
            new KeyValuePair<string, object>("operation", operation),
            new KeyValuePair<string, object>("error_type", exception.GetType().Name),
            new KeyValuePair<string, object>("severity", GetErrorSeverity(exception))
        });
    }
}

技術指標監控

public class TechnicalMetricsMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IMetricsCollector _metrics;

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        var path = context.Request.Path.Value;
        var method = context.Request.Method;

        try
        {
            await _next(context);
        }
        finally
        {
            stopwatch.Stop();

            // ✅ 請求指標API性能監控
            _metrics.Histogram("http.request.duration", stopwatch.ElapsedMilliseconds, new[]
            {
                new KeyValuePair<string, object>("method", method),
                new KeyValuePair<string, object>("path", path),
                new KeyValuePair<string, object>("status_code", context.Response.StatusCode)
            });

            // 記憶體使用指標
            var memoryUsage = GC.GetTotalMemory(false);
            _metrics.Gauge("system.memory.usage", memoryUsage);

            // 資料庫連接池指標
            _metrics.Gauge("database.connection_pool.active",
                GetActiveConnectionCount());
        }
    }
}

📚 程式碼品質標準

編碼規範

命名規範

// ✅ 清晰的命名:表達意圖,而非實現
public class SentenceAnalysisService  // 而非 AIService
{
    // 方法命名:動詞 + 名詞,表達業務意圖
    public async Task<AnalysisResult> AnalyzeEnglishSentenceAsync(string sentence)
    {
        // 變數命名:有意義的業務術語
        var grammarCheckResult = await CheckGrammarAsync(sentence);
        var vocabularyAnalysis = await AnalyzeVocabularyAsync(sentence);
        var idiomDetection = await DetectIdiomsAsync(sentence);

        return BuildAnalysisResult(grammarCheckResult, vocabularyAnalysis, idiomDetection);
    }

    // ✅ 私有方法:體現實現細節
    private async Task<GrammarCheckResult> CheckGrammarAsync(string sentence)
    {
        var prompt = _promptBuilder.BuildGrammarCheckPrompt(sentence);
        var aiResponse = await _aiProvider.CallAsync(prompt);
        return _grammarParser.Parse(aiResponse);
    }
}

錯誤處理規範

// ✅ 自訂異常:明確的錯誤分類
public abstract class DramaLingException : Exception
{
    public string ErrorCode { get; }
    public Dictionary<string, object> Context { get; }

    protected DramaLingException(string errorCode, string message,
        Dictionary<string, object>? context = null) : base(message)
    {
        ErrorCode = errorCode;
        Context = context ?? new Dictionary<string, object>();
    }
}

public class AIServiceException : DramaLingException
{
    public AIServiceException(string provider, string aiErrorMessage,
        string? originalPrompt = null)
        : base("AI_SERVICE_ERROR", $"AI service '{provider}' failed: {aiErrorMessage}")
    {
        Context["Provider"] = provider;
        Context["AIErrorMessage"] = aiErrorMessage;
        if (originalPrompt != null)
            Context["OriginalPrompt"] = originalPrompt;
    }
}

// 使用範例
public async Task<AnalysisResult> AnalyzeAsync(string inputText)
{
    try
    {
        return await _aiProvider.AnalyzeAsync(inputText);
    }
    catch (HttpRequestException ex) when (ex.Message.Contains("timeout"))
    {
        throw new AIServiceException(_aiProvider.Name, "Request timeout", inputText);
    }
    catch (JsonException ex)
    {
        throw new AIServiceException(_aiProvider.Name, "Invalid response format", inputText);
    }
}

🔮 未來擴展架構

AI模型演進支援

模型版本管理

public class AIModelManager
{
    private readonly Dictionary<string, IAIProvider> _providers;
    private readonly IConfiguration _configuration;

    // ✅ 版本控制支援AI模型A/B測試
    public async Task<AnalysisResult> AnalyzeWithVersionAsync(string inputText,
        string? modelVersion = null)
    {
        var version = modelVersion ?? GetDefaultModelVersion();
        var provider = GetProviderForVersion(version);

        var result = await provider.AnalyzeAsync(inputText);

        // 記錄模型性能
        await RecordModelPerformanceAsync(version, result);

        return result;
    }

    // ✅ 藍綠部署:無縫模型升級
    public async Task<bool> CanaryDeploymentAsync(string newModelVersion, double trafficPercentage)
    {
        var canaryProvider = GetProviderForVersion(newModelVersion);
        var testResults = await RunCanaryTestsAsync(canaryProvider);

        if (testResults.ErrorRate < 0.01 && testResults.PerformanceIndex > 0.95)
        {
            await GraduallyMigrateTrafficAsync(newModelVersion, trafficPercentage);
            return true;
        }

        return false;
    }
}

多租戶架構準備

租戶隔離設計

public class TenantContext
{
    public string TenantId { get; set; }
    public string DatabaseConnection { get; set; }
    public AIServiceConfiguration AIConfig { get; set; }
    public Dictionary<string, object> CustomSettings { get; set; }
}

public class MultiTenantAIService : IAIAnalysisService
{
    private readonly ITenantResolver _tenantResolver;
    private readonly IServiceProvider _serviceProvider;

    // ✅ 租戶隔離:每個租戶獨立配置
    public async Task<AnalysisResult> AnalyzeAsync(AnalysisRequest request)
    {
        var tenant = await _tenantResolver.GetCurrentTenantAsync();

        // 使用租戶特定的AI配置
        var aiProvider = _serviceProvider.GetKeyedService<IAIProvider>(tenant.AIConfig.Provider);

        // 使用租戶特定的提示詞範本
        var prompt = BuildTenantSpecificPrompt(request.InputText, tenant);

        return await aiProvider.AnalyzeAsync(prompt);
    }
}

📋 實施檢查清單

架構實施階段

Phase 1: 基礎架構 (第1-2週)

  • 建立分層架構基礎專案結構
  • 實作依賴注入容器配置
  • 建立基礎API控制器和中介軟體
  • 設定Entity Framework和資料庫遷移
  • 實作基本的健康檢查和日誌系統

Phase 2: AI整合 (第3-4週)

  • 實作AI提供商抽象層
  • 建立智能快取系統
  • 實作Prompt管理和版本控制
  • 建立錯誤處理和重試機制
  • 設定監控指標和告警

Phase 3: 優化與安全 (第5-6週)

  • 實作性能優化策略
  • 建立全面的安全防護
  • 完善測試套件 (單元/整合/E2E)
  • 設定CI/CD流程
  • 建立生產環境監控

品質檢查標準

程式碼品質

覆蓋率要求:
  - 單元測試覆蓋率: > 80%
  - 業務邏輯覆蓋率: > 90%
  - 關鍵路徑覆蓋率: 100%

性能基準:
  - API回應時間: P95 < 500ms
  - AI處理時間: P95 < 5s
  - 資料庫查詢: P95 < 100ms
  - 記憶體使用: < 500MB per instance

安全檢查:
  - 輸入驗證: 100%覆蓋
  - SQL注入防護: 已驗證
  - XSS防護: 已驗證
  - API金鑰安全: 已驗證

🎓 最佳實踐總結

核心原則

  1. 單一職責: 每個類別、方法都有明確單一的職責
  2. 依賴倒置: 依賴抽象而非具體實現
  3. 開放封閉: 對擴展開放,對修改封閉
  4. 介面隔離: 客戶端不應依賴不需要的介面
  5. 最小驚訝: 程式碼行為符合直覺期望

AI特定最佳實踐

  1. Prompt版本化: 將Prompt當作程式碼管理
  2. 多提供商支援: 避免供應商鎖定
  3. 智能快取: 減少昂貴的AI API調用
  4. 優雅降級: AI服務故障時的備援策略
  5. 成本監控: 追蹤和優化AI API使用成本

維護性保證

  1. 文檔驅動: 架構決策和變更都要記錄
  2. 自動化測試: 確保重構和擴展的安全性
  3. 監控完整: 從業務指標到技術指標全覆蓋
  4. 配置外部化: 所有環境特定配置都外部化
  5. 日誌結構化: 便於查詢、分析和告警

文件版本: v1.0 技術架構: .NET 8 + Entity Framework + AI整合 最後更新: 2025-01-25 下次審查: 2025-02-25

參考實現: DramaLing AI語言學習平台 適用範圍: 中小型AI驅動產品 (< 10萬用戶)