This document describes the architecture of the OxiCloud desktop/mobile sync client.
OxiCloud App is a cross-platform sync client built with:
- Flutter for the user interface (5 platforms: Windows, macOS, Linux, Android, iOS)
- Rust for the core sync engine (performance-critical operations)
- flutter_rust_bridge for FFI communication between Flutter and Rust
The application follows Clean Architecture principles, ensuring:
- Separation of concerns
- Testability
- Independence from frameworks
- Independence from UI
┌─────────────────────────────────────────────────────────────────┐
│ FLUTTER (Dart) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ PRESENTATION LAYER │ │
│ │ ┌─────────┐ ┌─────────┐ ┌──────────┐ │ │
│ │ │ Pages │ │ BLoCs │ │ Widgets │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬─────┘ │ │
│ └───────┼────────────┼────────────┼──────────────────────┘ │
│ │ │ │ │
│ ┌───────┴────────────┴────────────┴──────────────────────┐ │
│ │ DOMAIN LAYER │ │
│ │ ┌──────────┐ ┌──────────────┐ ┌─────────────┐ │ │
│ │ │ Entities │ │ Repositories │ │ Use Cases │ │ │
│ │ │ (pure) │ │ (abstract) │ │ (optional) │ │ │
│ │ └──────────┘ └──────────────┘ └─────────────┘ │ │
│ └───────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────┴────────────────────────────────┐ │
│ │ DATA LAYER │ │
│ │ ┌─────────────────────┐ ┌────────────────────────┐ │ │
│ │ │ Repository Impls │ │ Rust Bridge Source │ │ │
│ │ └─────────────────────┘ └───────────┬────────────┘ │ │
│ └───────────────────────────────────────┼────────────────┘ │
└──────────────────────────────────────────┼──────────────────────┘
│ FFI
┌──────────────────────────────────────────┼──────────────────────┐
│ RUST CORE │
│ ┌───────────────────────────────────────┴──────────────────┐ │
│ │ API Layer │ │
│ │ (flutter_rust_bridge) │ │
│ └───────────────────────────────────────┬──────────────────┘ │
│ │ │
│ ┌───────────────────────────────────────┴──────────────────┐ │
│ │ APPLICATION LAYER │ │
│ │ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐ │ │
│ │ │ SyncService │ │ AuthService │ │ FileWatcher │ │ │
│ │ └──────────────┘ └────────────────┘ └──────────────┘ │ │
│ └───────────────────────────────────────┬──────────────────┘ │
│ │ │
│ ┌───────────────────────────────────────┴──────────────────┐ │
│ │ DOMAIN LAYER │ │
│ │ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Entities │ │ Ports (Traits) │ │ │
│ │ └──────────┘ └──────────────────┘ │ │
│ └───────────────────────────────────────┬──────────────────┘ │
│ │ │
│ ┌───────────────────────────────────────┴──────────────────┐ │
│ │ INFRASTRUCTURE LAYER │ │
│ │ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐ │ │
│ │ │ WebDAV Client│ │ SQLite Storage │ │ File Watcher │ │ │
│ │ └──────────────┘ └────────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
- Pages: Full screen UI components (LoginPage, HomePage, SettingsPage)
- BLoCs: Business Logic Components for state management
- Widgets: Reusable UI components
- Entities: Core business objects (
User,SyncFolder,SyncStatus) - Repositories: Abstract interfaces (ports) for data operations
- Use Cases: Optional application-specific business rules
- Repository Implementations: Concrete implementations of domain interfaces
- RustBridgeDataSource: FFI calls to native Rust code
- FFI bindings generated by flutter_rust_bridge
OxiCloudApistruct exposing safe Rust functions to Dart
- SyncService: Orchestrates file synchronization
- AuthService: Handles authentication flows
- FileWatcherService: Monitors filesystem for changes
- Entities:
SyncItem,SyncStatus,AuthCredentials,SyncConfig - Ports: Trait definitions (
SyncPort,StoragePort,AuthPort)
- WebDavClient: HTTP client for WebDAV server communication
- SqliteStorage: Local database for sync state persistence
- NotifyFileWatcher: File system event monitoring
LoginPage → AuthBloc → AuthRepository → RustBridgeDataSource
↓
Rust API
↓
AuthService
↓
WebDavClient → Server
HomePage → SyncBloc → SyncRepository → RustBridgeDataSource
↓
Rust API
↓
SyncService
↙ ↘
SqliteStorage WebDavClient
↓ ↓
Local DB Server
The app uses BLoC (Business Logic Component) pattern:
- AuthBloc: Authentication state
- SyncBloc: Synchronization state and operations
- SettingsBloc: Application settings
Events flow in, states flow out:
UI → Event → BLoC → Repository → State → UI
Dependencies are managed with GetIt:
// Register
getIt.registerLazySingleton<AuthRepository>(() => AuthRepositoryImpl(...));
// Resolve
final authRepo = getIt<AuthRepository>();lib/
├── main.dart # Entry point
├── injection.dart # DI configuration
├── core/
│ ├── entities/ # Domain entities
│ ├── repositories/ # Repository interfaces
│ └── usecases/ # Use cases (optional)
├── data/
│ ├── datasources/ # Data sources
│ └── repositories/ # Repository implementations
└── presentation/
├── app.dart # App widget & routing
├── blocs/ # BLoC classes
├── pages/ # Screen widgets
└── widgets/ # Reusable components
rust/
├── Cargo.toml
└── src/
├── lib.rs
├── api.rs # FFI API
├── domain/
│ ├── entities/ # Rust entities
│ └── ports/ # Trait definitions
├── application/ # Services
└── infrastructure/ # Concrete implementations
Errors are represented using the Either type from dartz:
Future<Either<AuthFailure, User>> login(AuthCredentials credentials);Failure types are sealed classes:
sealed class AuthFailure {}
class InvalidCredentialsFailure implements AuthFailure {}
class ServerUnreachableFailure implements AuthFailure {
final String message;
ServerUnreachableFailure(this.message);
}- Unit Tests: Test individual classes in isolation
- Widget Tests: Test UI components
- Integration Tests: Test Flutter ↔ Rust communication
- E2E Tests: Full application flows
Mock repositories for testing:
class MockAuthRepository extends Mock implements AuthRepository {}