This document outlines the plan and implementation for modernizing Bundlingway's architecture to support multiple UI frameworks and improve separation of concerns.
- Decouple UI from Business Logic: Enable running with different UI frameworks (WinForms, Blazor, Electron, CLI)
- Improve Testability: Enable unit testing through dependency injection
- Enable Headless Operation: Support command-line and server scenarios
- Maintain Backward Compatibility: Ensure existing functionality continues to work during migration
Bundlingway.Core/
├── Interfaces/ # Service contracts
│ ├── IUserNotificationService.cs # UI-agnostic notifications
│ ├── IProgressReporter.cs # Progress reporting abstraction
│ ├── IConfigurationService.cs # Configuration management
│ ├── IPackageService.cs # Package operations
│ ├── IFileSystemService.cs # File operations abstraction
│ ├── IHttpClientService.cs # HTTP operations abstraction
│ └── IApplicationHost.cs # Application lifecycle
├── Services/ # Core business logic implementations
│ ├── ConfigurationService.cs # Config management implementation
│ ├── FileSystemService.cs # Standard file operations
│ ├── HttpClientService.cs # HTTP client wrapper
│ ├── BundlingwayService.cs # App update service
│ └── ServiceLocator.cs # Service discovery (interim)
└── Events/ # Application events and messaging
UI/
├── WinForms/ # Current WinForms implementation
│ ├── WinFormsNotificationService.cs # WinForms notifications
│ └── WinFormsProgressReporter.cs # WinForms progress UI
├── Blazor/ # Future Blazor web UI
└── Electron/ # Future Electron desktop UI
- Create core interfaces for major services
- Implement basic service implementations
- Set up service locator for dependency management
- Create WinForms bridge implementations
- Add modernized UI wrapper class
- Refactor Package handlers to use services
- Replace direct
UI.Announce()calls withIUserNotificationService - Replace direct
Instances.LocalConfigProviderwithIConfigurationService - Replace direct file operations with
IFileSystemService - Replace direct HTTP calls with
IHttpClientService
- Remove static UI dependencies from business logic
- Implement proper dependency injection container
- Create application host for service coordination
- Enable headless mode support
- Create Blazor UI implementation
- Create Electron UI implementation
- Create CLI interface for automation
- Implement UI plugin architecture
// Old approach (tightly coupled)
await UI.Announce("Processing...");
await Package.Onboard(filePath);
// New approach (decoupled)
var notificationService = ServiceLocator.GetService<IUserNotificationService>();
var packageService = ServiceLocator.GetService<IPackageService>();
await notificationService.AnnounceAsync("Processing...");
await packageService.OnboardPackageAsync(filePath);The new ModernUI class provides backward compatibility:
// This still works during migration
await ModernUI.Announce("Processing..."); // Uses new services if available, falls back to legacy UIpublic class MyNewService
{
private readonly IConfigurationService _config;
private readonly IUserNotificationService _notifications;
public MyNewService(IConfigurationService config, IUserNotificationService notifications)
{
_config = config;
_notifications = notifications;
}
public async Task DoSomethingAsync()
{
await _notifications.AnnounceAsync("Starting operation...");
// Business logic here
await _config.SaveAsync();
}
}With the new architecture, you can easily create unit tests:
[Test]
public async Task Should_Update_Configuration_When_Processing()
{
// Arrange
var mockConfig = new Mock<IConfigurationService>();
var mockNotifications = new Mock<IUserNotificationService>();
var service = new MyNewService(mockConfig.Object, mockNotifications.Object);
// Act
await service.DoSomethingAsync();
// Assert
mockConfig.Verify(x => x.SaveAsync(), Times.Once);
mockNotifications.Verify(x => x.AnnounceAsync("Starting operation..."), Times.Once);
}public class BlazorNotificationService : IUserNotificationService
{
public async Task AnnounceAsync(string message)
{
// Use Blazor's notification system
await JSRuntime.InvokeVoidAsync("showNotification", message);
}
}public class ElectronNotificationService : IUserNotificationService
{
public async Task AnnounceAsync(string message)
{
// Use Electron's notification API
await ElectronNET.API.Electron.Notification.Show(new NotificationOptions(message));
}
}public class ConsoleNotificationService : IUserNotificationService
{
public async Task AnnounceAsync(string message)
{
Console.WriteLine($"[INFO] {message}");
await Task.CompletedTask;
}
}- Core service interfaces
- Basic service implementations
- Service locator pattern
- WinForms bridge services
- Modern UI wrapper class
- Example modernized Bundlingway service
- Gradual migration of existing handlers
- Integration with existing codebase
- Migrate Package handler methods one by one
- Replace UI calls throughout the codebase
- Add comprehensive unit tests
- Implement proper DI container
- Create alternative UI implementations
- Testability: Services can now be unit tested in isolation
- Flexibility: UI can be swapped without changing business logic
- Maintainability: Clear separation of concerns
- Extensibility: Easy to add new features and UI types
- Future-Proofing: Architecture supports modern development practices
This modernization maintains all existing functionality while providing a clear path to multiple UI frameworks and better software architecture.