The solution uses the application executable as the composition root. Lower layers do not resolve services from an
IServiceProvider; dependencies are supplied through constructors or narrow capability interfaces.
KeyAsio.Commoncontains implementation-independent utilities and collections.KeyAsio.Plugins.Contractsis the only API surface plugins should compile against. It depends only on stable public data/audio packages and never exposes the host service container.KeyAsio.Configurationowns persisted settings and their serialization.KeyAsio.Core.*owns audio, memory and beatmap-audio primitives.KeyAsio.Syncowns game-state acquisition and synchronized playback orchestration.KeyAsio.Applicationowns host runtime services, plugin loading, localization and skin discovery.KeyAsioowns Avalonia/Suki presentation, OS integration and dependency injection composition.
Dependencies point inward toward contracts and primitives. In particular, KeyAsio.Sync has no dependency on
KeyAsio.Application, and neither plugin contracts nor application services depend on Avalonia/Suki.
Synchronization extensions follow dependency inversion: KeyAsio.Sync owns the narrow ISyncExtensionHost port,
while KeyAsio.Application implements it with SyncPluginCoordinator. The coordinator adapts SyncSessionContext
to the read-only plugin contract and owns plugin callback dispatch, propagation rules and exception isolation.
All application-level device changes go through IAudioDeviceOperationCoordinator. A transition is serialized,
stops the current device, starts the requested device, and only then commits settings. A failed start or persistence
operation restores both the previous runtime device and the previous persisted configuration. Cache invalidation is a
post-transition concern and cannot corrupt the transaction.
The contract is intentionally breaking. A plugin project should reference KeyAsio.Plugins.Contracts and must not
reference KeyAsio.Application, KeyAsio.Configuration, KeyAsio.Sync, Avalonia host services, Suki managers or
Octokit.
| Previous dependency | Contract capability |
|---|---|
Root IServiceProvider |
Explicit properties on IPluginContext |
AppSettings |
IPluginSettings |
GameplaySessionManager / SyncSessionContext |
IGameplaySession |
AudioCacheManager / CachedAudio |
IGameplaySession.TryCreateCachedAudioProvider |
| Beatmap resource catalog implementation | TryResolveResource / TryResolveAudioResource |
| Dispatcher, dialog and toast managers | IPluginInteractionService |
| Logger resolved from DI | IPluginContext.LoggerFactory |
Octokit.Release |
UpdateRelease and UpdateAsset |
The host shares already-loaded assemblies with each collectible plugin load context and resolves plugin-private managed and native dependencies from the plugin output directory. This preserves type identity for the contract while keeping plugin-only dependencies unloadable.