50 KiB
50 KiB
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金鑰安全: 已驗證
🎓 最佳實踐總結
核心原則
- 單一職責: 每個類別、方法都有明確單一的職責
- 依賴倒置: 依賴抽象而非具體實現
- 開放封閉: 對擴展開放,對修改封閉
- 介面隔離: 客戶端不應依賴不需要的介面
- 最小驚訝: 程式碼行為符合直覺期望
AI特定最佳實踐
- Prompt版本化: 將Prompt當作程式碼管理
- 多提供商支援: 避免供應商鎖定
- 智能快取: 減少昂貴的AI API調用
- 優雅降級: AI服務故障時的備援策略
- 成本監控: 追蹤和優化AI API使用成本
維護性保證
- 文檔驅動: 架構決策和變更都要記錄
- 自動化測試: 確保重構和擴展的安全性
- 監控完整: 從業務指標到技術指標全覆蓋
- 配置外部化: 所有環境特定配置都外部化
- 日誌結構化: 便於查詢、分析和告警
文件版本: v1.0 技術架構: .NET 8 + Entity Framework + AI整合 最後更新: 2025-01-25 下次審查: 2025-02-25
參考實現: DramaLing AI語言學習平台 適用範圍: 中小型AI驅動產品 (< 10萬用戶)