项目架构
本章将深入分析 Verdure Assistant 的系统架构设计,帮助您理解项目的设计理念、模块组织和扩展机制。
🏗️ 整体架构概览
Verdure Assistant 采用分层架构设计,结合依赖注入和模块化原则,实现了高内聚、低耦合的系统结构。
mermaid
graph TB
subgraph "表现层 (Presentation Layer)"
A[WinUI 应用]
B[MAUI 应用]
C[Console 应用]
D[Web API]
end
subgraph "应用服务层 (Application Layer)"
E[ViewModel]
F[Controllers]
G[Command Handlers]
H[Services Coordination]
end
subgraph "领域服务层 (Domain Services)"
I[语音聊天服务]
J[音频处理服务]
K[配置管理服务]
L[状态管理服务]
end
subgraph "基础设施层 (Infrastructure Layer)"
M[WebSocket 客户端]
N[MQTT 客户端]
O[音频编解码器]
P[文件存储]
Q[日志记录]
end
subgraph "核心层 (Core Layer)"
R[领域模型]
S[接口定义]
T[常量定义]
U[扩展方法]
end
A --> E
B --> E
C --> G
D --> F
E --> I
F --> I
G --> I
I --> M
J --> O
K --> P
M --> R
N --> R
O --> R
📁 项目结构分析
核心项目组织
src/
├── Verdure.Assistant.Core/ # 核心库 - 领域模型和接口
├── Verdure.Assistant.ViewModels/ # 视图模型 - MVVM 支持
├── Verdure.Assistant.Console/ # 控制台应用
├── Verdure.Assistant.WinUI/ # Windows 桌面应用
├── Verdure.Assistant.MAUI/ # 跨平台移动应用
└── Verdure.Assistant.Api/ # Web API 服务
核心库(Core)设计
Core 项目是整个系统的基础,定义了领域模型和核心接口:
csharp
namespace Verdure.Assistant.Core
{
// 领域模型
public record ChatMessage(
string Id,
string Content,
MessageType Type,
DateTime Timestamp,
string? UserId = null);
public enum MessageType
{
User,
Assistant,
System,
Audio
}
public enum DeviceState
{
Disconnected,
Connecting,
Connected,
Listening,
Speaking,
Processing,
Error
}
// 核心接口定义
public interface IVoiceChatService
{
event EventHandler<ChatMessage>? MessageReceived;
event EventHandler<DeviceState>? StateChanged;
DeviceState CurrentState { get; }
bool IsConnected { get; }
Task StartVoiceChatAsync();
Task StopVoiceChatAsync();
Task SendTextMessageAsync(string text);
Task<bool> SwitchKeywordModelAsync(string modelName);
}
public interface IAudioCodec
{
byte[] Encode(byte[] pcmData, int sampleRate, int channels);
byte[] Decode(byte[] encodedData, int sampleRate, int channels);
}
public interface IWebSocketClient
{
event EventHandler<string>? MessageReceived;
event EventHandler<bool>? ConnectionStateChanged;
bool IsConnected { get; }
Task ConnectAsync(Uri serverUri);
Task DisconnectAsync();
Task SendAsync(string message);
}
}
🎯 设计模式应用
1. 依赖注入模式
项目广泛使用依赖注入实现控制反转:
csharp
// 服务注册 (Program.cs)
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddVerdureServices(this IServiceCollection services)
{
// 核心服务
services.AddSingleton<IAudioCodec, OpusCodec>();
services.AddSingleton<IWebSocketClient, WebSocketClient>();
services.AddScoped<IVoiceChatService, VoiceChatService>();
// 音频服务
services.AddSingleton<IAudioCaptureService, PortAudioCaptureService>();
services.AddSingleton<IAudioPlaybackService, PortAudioPlaybackService>();
// 配置服务
services.AddSingleton<IConfigurationService, JsonConfigurationService>();
services.AddSingleton<ISettingsService, SettingsService>();
return services;
}
}
// 服务使用
public class VoiceChatService : IVoiceChatService
{
private readonly IAudioCodec _audioCodec;
private readonly IWebSocketClient _webSocketClient;
private readonly ILogger<VoiceChatService> _logger;
public VoiceChatService(
IAudioCodec audioCodec,
IWebSocketClient webSocketClient,
ILogger<VoiceChatService> logger)
{
_audioCodec = audioCodec;
_webSocketClient = webSocketClient;
_logger = logger;
}
}
2. 观察者模式
用于事件通知和状态更新:
csharp
public class VoiceChatService : IVoiceChatService
{
public event EventHandler<ChatMessage>? MessageReceived;
public event EventHandler<DeviceState>? StateChanged;
private DeviceState _currentState = DeviceState.Disconnected;
public DeviceState CurrentState
{
get => _currentState;
private set
{
if (_currentState != value)
{
_currentState = value;
StateChanged?.Invoke(this, value);
_logger.LogInformation("状态变更: {State}", value);
}
}
}
private void HandleWebSocketMessage(string message)
{
var chatMessage = JsonSerializer.Deserialize<ChatMessage>(message);
MessageReceived?.Invoke(this, chatMessage);
}
}
3. 策略模式
用于不同平台的实现:
csharp
public interface IAudioPlatformProvider
{
Task<IAudioDevice[]> GetInputDevicesAsync();
Task<IAudioDevice[]> GetOutputDevicesAsync();
IAudioCapture CreateAudioCapture(IAudioDevice device);
IAudioPlayback CreateAudioPlayback(IAudioDevice device);
}
#if WINDOWS
public class WindowsAudioProvider : IAudioPlatformProvider
{
public async Task<IAudioDevice[]> GetInputDevicesAsync()
{
// Windows 特定实现
return await GetWasapiInputDevicesAsync();
}
public IAudioCapture CreateAudioCapture(IAudioDevice device)
{
return new WasapiAudioCapture(device);
}
}
#elif ANDROID
public class AndroidAudioProvider : IAudioPlatformProvider
{
public async Task<IAudioDevice[]> GetInputDevicesAsync()
{
// Android 特定实现
return await GetAndroidAudioDevicesAsync();
}
public IAudioCapture CreateAudioCapture(IAudioDevice device)
{
return new AndroidAudioCapture(device);
}
}
#endif
4. 工厂模式
用于创建复杂对象:
csharp
public interface IVoiceChatServiceFactory
{
IVoiceChatService Create(VoiceChatConfig config);
}
public class VoiceChatServiceFactory : IVoiceChatServiceFactory
{
private readonly IServiceProvider _serviceProvider;
public VoiceChatServiceFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IVoiceChatService Create(VoiceChatConfig config)
{
// 根据配置创建不同的服务实例
var audioCodec = config.CodecType switch
{
CodecType.Opus => _serviceProvider.GetRequiredService<OpusCodec>(),
CodecType.PCM => _serviceProvider.GetRequiredService<PcmCodec>(),
_ => throw new ArgumentException($"不支持的编解码器类型: {config.CodecType}")
};
var webSocketClient = new WebSocketClient(config.ServerUrl);
return new VoiceChatService(
audioCodec,
webSocketClient,
_serviceProvider.GetRequiredService<ILogger<VoiceChatService>>());
}
}
🔄 状态管理架构
状态机设计
语音聊天服务使用状态机模式管理复杂的状态转换:
csharp
public class VoiceStateMachine
{
private readonly Dictionary<(DeviceState From, VoiceEvent Event), DeviceState> _transitions;
public VoiceStateMachine()
{
_transitions = new Dictionary<(DeviceState, VoiceEvent), DeviceState>
{
{ (DeviceState.Disconnected, VoiceEvent.Connect), DeviceState.Connecting },
{ (DeviceState.Connecting, VoiceEvent.Connected), DeviceState.Connected },
{ (DeviceState.Connected, VoiceEvent.StartListening), DeviceState.Listening },
{ (DeviceState.Listening, VoiceEvent.VoiceDetected), DeviceState.Processing },
{ (DeviceState.Processing, VoiceEvent.ResponseReceived), DeviceState.Speaking },
{ (DeviceState.Speaking, VoiceEvent.SpeechFinished), DeviceState.Connected },
// ... 更多状态转换
};
}
public bool CanTransition(DeviceState from, VoiceEvent @event)
{
return _transitions.ContainsKey((from, @event));
}
public DeviceState GetNextState(DeviceState from, VoiceEvent @event)
{
if (_transitions.TryGetValue((from, @event), out var nextState))
{
return nextState;
}
throw new InvalidOperationException($"无法从状态 {from} 通过事件 {@event} 进行转换");
}
}
public enum VoiceEvent
{
Connect,
Connected,
Disconnect,
StartListening,
StopListening,
VoiceDetected,
VoiceEnded,
ResponseReceived,
SpeechFinished,
Error
}
全局状态管理
csharp
public class ApplicationState
{
private readonly ConcurrentDictionary<string, object> _state = new();
private readonly Subject<StateChange> _stateChanges = new();
public IObservable<StateChange> StateChanges => _stateChanges.AsObservable();
public T GetValue<T>(string key, T defaultValue = default)
{
if (_state.TryGetValue(key, out var value) && value is T typedValue)
{
return typedValue;
}
return defaultValue;
}
public void SetValue<T>(string key, T value)
{
var oldValue = GetValue<T>(key);
_state.AddOrUpdate(key, value, (k, v) => value);
_stateChanges.OnNext(new StateChange
{
Key = key,
OldValue = oldValue,
NewValue = value,
Timestamp = DateTime.UtcNow
});
}
}
public record StateChange
{
public string Key { get; init; }
public object? OldValue { get; init; }
public object? NewValue { get; init; }
public DateTime Timestamp { get; init; }
}
🌐 通信架构
WebSocket 通信层
csharp
public class WebSocketManager
{
private ClientWebSocket? _webSocket;
private readonly SemaphoreSlim _sendSemaphore = new(1, 1);
private readonly CancellationTokenSource _cancellationTokenSource = new();
public event EventHandler<WebSocketMessage>? MessageReceived;
public event EventHandler<WebSocketState>? StateChanged;
public async Task ConnectAsync(Uri serverUri)
{
_webSocket = new ClientWebSocket();
// 配置 WebSocket
_webSocket.Options.SetRequestHeader("User-Agent", "Verdure-Assistant/1.0");
_webSocket.Options.KeepAliveInterval = TimeSpan.FromSeconds(30);
await _webSocket.ConnectAsync(serverUri, _cancellationTokenSource.Token);
// 开始监听消息
_ = Task.Run(ReceiveMessagesAsync, _cancellationTokenSource.Token);
StateChanged?.Invoke(this, _webSocket.State);
}
public async Task SendAsync<T>(T message) where T : class
{
if (_webSocket?.State != WebSocketState.Open)
{
throw new InvalidOperationException("WebSocket 未连接");
}
await _sendSemaphore.WaitAsync();
try
{
var json = JsonSerializer.Serialize(message);
var buffer = Encoding.UTF8.GetBytes(json);
await _webSocket.SendAsync(
new ArraySegment<byte>(buffer),
WebSocketMessageType.Text,
true,
_cancellationTokenSource.Token);
}
finally
{
_sendSemaphore.Release();
}
}
private async Task ReceiveMessagesAsync()
{
var buffer = new byte[8192];
while (_webSocket?.State == WebSocketState.Open && !_cancellationTokenSource.Token.IsCancellationRequested)
{
try
{
var result = await _webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer),
_cancellationTokenSource.Token);
if (result.MessageType == WebSocketMessageType.Text)
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
var webSocketMessage = JsonSerializer.Deserialize<WebSocketMessage>(message);
MessageReceived?.Invoke(this, webSocketMessage);
}
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
// 处理接收错误
OnError(ex);
}
}
}
}
消息路由系统
csharp
public class MessageRouter
{
private readonly Dictionary<string, List<IMessageHandler>> _handlers = new();
public void RegisterHandler<T>(IMessageHandler<T> handler) where T : class
{
var messageType = typeof(T).Name;
if (!_handlers.ContainsKey(messageType))
{
_handlers[messageType] = new List<IMessageHandler>();
}
_handlers[messageType].Add(handler);
}
public async Task RouteMessageAsync(WebSocketMessage message)
{
if (_handlers.TryGetValue(message.Type, out var handlers))
{
var tasks = handlers.Select(handler => handler.HandleAsync(message));
await Task.WhenAll(tasks);
}
}
}
public interface IMessageHandler
{
Task HandleAsync(WebSocketMessage message);
}
public interface IMessageHandler<T> : IMessageHandler where T : class
{
Task HandleAsync(T message);
async Task IMessageHandler.HandleAsync(WebSocketMessage message)
{
if (JsonSerializer.Deserialize<T>(message.Payload) is T typedMessage)
{
await HandleAsync(typedMessage);
}
}
}
🎤 音频处理架构
音频管道设计
csharp
public class AudioPipeline
{
private readonly List<IAudioProcessor> _processors = new();
public AudioPipeline AddProcessor(IAudioProcessor processor)
{
_processors.Add(processor);
return this;
}
public async Task<byte[]> ProcessAsync(byte[] audioData)
{
byte[] result = audioData;
foreach (var processor in _processors)
{
result = await processor.ProcessAsync(result);
}
return result;
}
}
public interface IAudioProcessor
{
Task<byte[]> ProcessAsync(byte[] audioData);
}
// 具体处理器实现
public class NoiseReductionProcessor : IAudioProcessor
{
public async Task<byte[]> ProcessAsync(byte[] audioData)
{
// 噪声抑制算法实现
return await ApplyNoiseReductionAsync(audioData);
}
}
public class VolumeNormalizationProcessor : IAudioProcessor
{
private readonly float _targetVolume;
public VolumeNormalizationProcessor(float targetVolume = 0.8f)
{
_targetVolume = targetVolume;
}
public async Task<byte[]> ProcessAsync(byte[] audioData)
{
// 音量标准化处理
return await NormalizeVolumeAsync(audioData, _targetVolume);
}
}
音频缓冲管理
csharp
public class CircularAudioBuffer
{
private readonly byte[] _buffer;
private readonly int _capacity;
private int _writeIndex;
private int _readIndex;
private int _count;
private readonly object _lock = new();
public CircularAudioBuffer(int capacity)
{
_capacity = capacity;
_buffer = new byte[capacity];
}
public bool Write(byte[] data)
{
lock (_lock)
{
if (_count + data.Length > _capacity)
{
return false; // 缓冲区满
}
for (int i = 0; i < data.Length; i++)
{
_buffer[_writeIndex] = data[i];
_writeIndex = (_writeIndex + 1) % _capacity;
}
_count += data.Length;
return true;
}
}
public byte[] Read(int length)
{
lock (_lock)
{
length = Math.Min(length, _count);
var result = new byte[length];
for (int i = 0; i < length; i++)
{
result[i] = _buffer[_readIndex];
_readIndex = (_readIndex + 1) % _capacity;
}
_count -= length;
return result;
}
}
public int AvailableBytes => _count;
public int FreeBytes => _capacity - _count;
}
📱 UI 架构设计
MVVM 架构实现
csharp
// ViewModelBase 基类
public abstract class ViewModelBase : ObservableObject
{
protected readonly ILogger _logger;
protected ViewModelBase(ILogger logger)
{
_logger = logger;
}
public virtual Task InitializeAsync() => Task.CompletedTask;
public virtual Task LoadedAsync() => Task.CompletedTask;
public virtual Task UnloadedAsync() => Task.CompletedTask;
protected virtual void OnError(Exception exception)
{
_logger.LogError(exception, "ViewModel 发生错误");
// 可以通过事件通知 UI 显示错误信息
}
}
// 具体 ViewModel 实现
public partial class HomePageViewModel : ViewModelBase
{
private readonly IVoiceChatService _voiceChatService;
[ObservableProperty]
private ObservableCollection<ChatMessage> _messages = new();
[ObservableProperty]
private string _connectionStatus = "未连接";
[ObservableProperty]
private bool _isVoiceActive;
public HomePageViewModel(
IVoiceChatService voiceChatService,
ILogger<HomePageViewModel> logger) : base(logger)
{
_voiceChatService = voiceChatService;
_voiceChatService.MessageReceived += OnMessageReceived;
_voiceChatService.StateChanged += OnStateChanged;
}
public override async Task InitializeAsync()
{
try
{
await _voiceChatService.InitializeAsync();
}
catch (Exception ex)
{
OnError(ex);
}
}
[RelayCommand]
private async Task ToggleVoiceAsync()
{
try
{
if (IsVoiceActive)
{
await _voiceChatService.StopVoiceChatAsync();
}
else
{
await _voiceChatService.StartVoiceChatAsync();
}
}
catch (Exception ex)
{
OnError(ex);
}
}
private void OnMessageReceived(object? sender, ChatMessage message)
{
// 在 UI 线程上更新集合
Application.Current.Dispatcher.Invoke(() =>
{
Messages.Add(message);
});
}
private void OnStateChanged(object? sender, DeviceState state)
{
IsVoiceActive = state == DeviceState.Listening;
ConnectionStatus = GetStatusText(state);
}
}
导航管理
csharp
public interface INavigationService
{
Task NavigateToAsync<T>() where T : class;
Task NavigateToAsync<T>(object parameter) where T : class;
Task GoBackAsync();
bool CanGoBack { get; }
}
public class NavigationService : INavigationService
{
private readonly Stack<Type> _navigationStack = new();
private readonly IServiceProvider _serviceProvider;
public bool CanGoBack => _navigationStack.Count > 1;
public async Task NavigateToAsync<T>() where T : class
{
await NavigateToAsync<T>(null);
}
public async Task NavigateToAsync<T>(object parameter) where T : class
{
var pageType = typeof(T);
var page = _serviceProvider.GetRequiredService<T>();
if (page is Page wpfPage)
{
// WPF/WinUI 导航
await NavigateToPageAsync(wpfPage, parameter);
}
else if (page is ContentPage mauiPage)
{
// MAUI 导航
await Shell.Current.GoToAsync(pageType.Name,
parameter != null ? new Dictionary<string, object> { ["Parameter"] = parameter } : null);
}
_navigationStack.Push(pageType);
}
public async Task GoBackAsync()
{
if (CanGoBack)
{
_navigationStack.Pop();
var previousPageType = _navigationStack.Peek();
// 执行返回导航逻辑
await NavigateBackToTypeAsync(previousPageType);
}
}
}
🔧 配置管理架构
分层配置系统
csharp
public interface IConfigurationProvider
{
int Priority { get; }
Task<T> GetValueAsync<T>(string key);
Task SetValueAsync<T>(string key, T value);
Task<bool> ContainsKeyAsync(string key);
}
public class ConfigurationManager
{
private readonly List<IConfigurationProvider> _providers = new();
public void AddProvider(IConfigurationProvider provider)
{
_providers.Add(provider);
_providers.Sort((x, y) => y.Priority.CompareTo(x.Priority));
}
public async Task<T> GetValueAsync<T>(string key, T defaultValue = default)
{
foreach (var provider in _providers)
{
if (await provider.ContainsKeyAsync(key))
{
return await provider.GetValueAsync<T>(key);
}
}
return defaultValue;
}
public async Task SetValueAsync<T>(string key, T value)
{
// 使用优先级最高的可写提供程序
var writableProvider = _providers.FirstOrDefault(p => p is IWritableConfigurationProvider);
if (writableProvider is IWritableConfigurationProvider writable)
{
await writable.SetValueAsync(key, value);
}
}
}
// 具体提供程序实现
public class EnvironmentConfigurationProvider : IConfigurationProvider
{
public int Priority => 100; // 环境变量优先级最高
public Task<T> GetValueAsync<T>(string key)
{
var value = Environment.GetEnvironmentVariable(key);
if (value != null)
{
return Task.FromResult(ConvertValue<T>(value));
}
throw new KeyNotFoundException($"环境变量 {key} 不存在");
}
public Task<bool> ContainsKeyAsync(string key)
{
return Task.FromResult(Environment.GetEnvironmentVariable(key) != null);
}
}
public class JsonFileConfigurationProvider : IConfigurationProvider, IWritableConfigurationProvider
{
public int Priority => 50;
private readonly string _filePath;
private Dictionary<string, object> _config = new();
public async Task<T> GetValueAsync<T>(string key)
{
await LoadConfigIfNeededAsync();
if (_config.TryGetValue(key, out var value))
{
return ConvertValue<T>(value);
}
throw new KeyNotFoundException($"配置项 {key} 不存在");
}
public async Task SetValueAsync<T>(string key, T value)
{
await LoadConfigIfNeededAsync();
_config[key] = value;
await SaveConfigAsync();
}
}
🧪 测试架构
测试分层结构
csharp
// 单元测试基类
public abstract class UnitTestBase
{
protected readonly IServiceProvider ServiceProvider;
protected readonly Mock<ILogger> MockLogger;
protected UnitTestBase()
{
var services = new ServiceCollection();
ConfigureServices(services);
ServiceProvider = services.BuildServiceProvider();
MockLogger = new Mock<ILogger>();
}
protected virtual void ConfigureServices(IServiceCollection services)
{
// 注册测试用的模拟服务
services.AddSingleton(MockLogger.Object);
}
protected T GetService<T>() => ServiceProvider.GetRequiredService<T>();
protected Mock<T> GetMock<T>() where T : class => Mock.Get(GetService<T>());
}
// 具体测试类
public class VoiceChatServiceTests : UnitTestBase
{
private readonly Mock<IAudioCodec> _mockAudioCodec;
private readonly Mock<IWebSocketClient> _mockWebSocketClient;
private readonly VoiceChatService _sut; // System Under Test
public VoiceChatServiceTests()
{
_mockAudioCodec = new Mock<IAudioCodec>();
_mockWebSocketClient = new Mock<IWebSocketClient>();
_sut = new VoiceChatService(
_mockAudioCodec.Object,
_mockWebSocketClient.Object,
MockLogger.Object);
}
[Fact]
public async Task StartVoiceChatAsync_WhenCalled_ShouldConnectWebSocket()
{
// Arrange
_mockWebSocketClient.Setup(x => x.ConnectAsync(It.IsAny<Uri>()))
.Returns(Task.CompletedTask);
// Act
await _sut.StartVoiceChatAsync();
// Assert
_mockWebSocketClient.Verify(x => x.ConnectAsync(It.IsAny<Uri>()), Times.Once);
Assert.Equal(DeviceState.Connected, _sut.CurrentState);
}
}
集成测试框架
csharp
public class IntegrationTestFixture : IDisposable
{
public WebApplicationFactory<Program> Factory { get; }
public HttpClient HttpClient { get; }
public IntegrationTestFixture()
{
Factory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
// 替换真实服务为测试用的模拟服务
services.RemoveAll<IVoiceChatService>();
services.AddSingleton<Mock<IVoiceChatService>>();
services.AddSingleton<IVoiceChatService>(provider =>
provider.GetRequiredService<Mock<IVoiceChatService>>().Object);
});
});
HttpClient = Factory.CreateClient();
}
public void Dispose()
{
HttpClient?.Dispose();
Factory?.Dispose();
}
}
🔍 性能监控架构
性能指标收集
csharp
public class PerformanceMetrics
{
private readonly ConcurrentDictionary<string, PerformanceCounter> _counters = new();
public void IncrementCounter(string name, double value = 1.0)
{
var counter = _counters.GetOrAdd(name, _ => new PerformanceCounter());
counter.Increment(value);
}
public void RecordLatency(string operation, TimeSpan duration)
{
var counter = _counters.GetOrAdd($"{operation}_latency", _ => new PerformanceCounter());
counter.RecordValue(duration.TotalMilliseconds);
}
public PerformanceReport GenerateReport()
{
return new PerformanceReport
{
Timestamp = DateTime.UtcNow,
Counters = _counters.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.GetSnapshot())
};
}
}
// 性能监控中间件
public class PerformanceMonitoringMiddleware
{
private readonly RequestDelegate _next;
private readonly PerformanceMetrics _metrics;
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
try
{
await _next(context);
_metrics.IncrementCounter("requests_success");
}
catch (Exception)
{
_metrics.IncrementCounter("requests_error");
throw;
}
finally
{
stopwatch.Stop();
_metrics.RecordLatency("request_duration", stopwatch.Elapsed);
}
}
}
📚 下一步学习
理解了项目架构后,您可以:
通过深入理解这些架构设计,您将能够:
- 理解现代 .NET 应用的设计理念
- 掌握分层架构和设计模式的实际应用
- 学会如何构建可测试、可维护的代码
- 了解如何处理复杂的状态管理和异步编程场景
这些架构知识不仅适用于 Verdure Assistant,也是现代软件开发的通用最佳实践。