perf: replace dyn trait objects with concrete types to eliminate vtable overhead

Remove async-trait dependency and use native Rust async fn in traits.
Replace Arc<dyn Trait> with Arc<ConcreteType> throughout the codebase
to enable monomorphization and eliminate dynamic dispatch overhead.

Key changes:
- Remove write-behind cache (no implementation existed)
- Fix should_transcode static method call
- Use ContactStorageAdapter directly instead of dyn AddressBookUseCase
- Clean up unused trait imports across services and DI

https://claude.ai/code/session_01EbAFEfyJNLRmJHmmYDX3Tt
This commit is contained in:
Claude
2026-03-03 15:36:42 +00:00
parent efcf88c4d7
commit 1b49135ca9
101 changed files with 346 additions and 576 deletions
-7
View File
@@ -3,7 +3,6 @@ use crate::domain::entities::app_password::AppPassword;
use crate::domain::entities::device_code::DeviceCode;
use crate::domain::entities::session::Session;
use crate::domain::entities::user::User;
use async_trait::async_trait;
// ============================================================================
// Cryptography Ports - Extracted from Domain to maintain Clean Architecture
@@ -16,7 +15,6 @@ use async_trait::async_trait;
///
/// Methods are async because implementations (e.g. Argon2) are CPU-intensive
/// and must run on a blocking thread pool to avoid starving Tokio workers.
#[async_trait]
pub trait PasswordHasherPort: Send + Sync + 'static {
/// Hash a plain text password
async fn hash_password(&self, password: &str) -> Result<String, DomainError>;
@@ -69,7 +67,6 @@ pub trait TokenServicePort: Send + Sync + 'static {
// Storage Ports
// ============================================================================
#[async_trait]
pub trait UserStoragePort: Send + Sync + 'static {
/// Creates a new user
async fn create_user(&self, user: User) -> Result<User, DomainError>;
@@ -153,7 +150,6 @@ pub struct OidcIdClaims {
}
/// Port for OIDC operations — implemented in infrastructure layer
#[async_trait]
pub trait OidcServicePort: Send + Sync + 'static {
/// Get the authorization URL for redirecting the user to the IdP.
/// Includes PKCE code_challenge (S256) and nonce for ID token binding.
@@ -187,7 +183,6 @@ pub trait OidcServicePort: Send + Sync + 'static {
fn provider_name(&self) -> &str;
}
#[async_trait]
pub trait SessionStoragePort: Send + Sync + 'static {
/// Creates a new session
async fn create_session(&self, session: Session) -> Result<Session, DomainError>;
@@ -209,7 +204,6 @@ pub trait SessionStoragePort: Send + Sync + 'static {
// Device Authorization Grant Port (RFC 8628)
// ============================================================================
#[async_trait]
pub trait DeviceCodeStoragePort: Send + Sync + 'static {
/// Persist a new device code flow
async fn create_device_code(&self, device_code: DeviceCode) -> Result<DeviceCode, DomainError>;
@@ -238,7 +232,6 @@ pub trait DeviceCodeStoragePort: Send + Sync + 'static {
// ============================================================================
/// Storage port for application-specific passwords (HTTP Basic Auth for DAV clients).
#[async_trait]
pub trait AppPasswordStoragePort: Send + Sync + 'static {
/// Persist a new app password (hash already computed).
async fn create(&self, app_password: AppPassword) -> Result<AppPassword, DomainError>;
-4
View File
@@ -9,7 +9,6 @@
//! implementation details.
use crate::common::errors::DomainError;
use async_trait::async_trait;
use bytes::Bytes;
use std::path::{Path, PathBuf};
use std::sync::Arc;
@@ -30,7 +29,6 @@ pub struct WriteBehindStatsDto {
/// Provides deferred write semantics: small files are held in memory
/// and the response is returned immediately, while actual disk writes
/// happen asynchronously in the background.
#[async_trait]
pub trait WriteBehindCachePort: Send + Sync + 'static {
/// Check if a file size is eligible for write-behind caching.
fn is_eligible_size(&self, size: usize) -> bool;
@@ -82,7 +80,6 @@ pub struct CachedMetadataDto {
///
/// Provides fast lookups for existence, size, timestamps and MIME types
/// without hitting the filesystem on every request.
#[async_trait]
pub trait MetadataCachePort: Send + Sync + 'static {
/// Get cached metadata for a path, or `None` on miss / expired.
async fn get_metadata(&self, path: &Path) -> Option<CachedMetadataDto>;
@@ -106,7 +103,6 @@ pub trait MetadataCachePort: Send + Sync + 'static {
///
/// Implementations should use LRU eviction and respect size limits so that
/// the application layer never needs to know the concrete cache type.
#[async_trait]
pub trait ContentCachePort: Send + Sync + 'static {
/// Check whether a file of the given size should be cached.
fn should_cache(&self, size: usize) -> bool;
-3
View File
@@ -3,11 +3,9 @@ use crate::application::dtos::calendar_dto::{
UpdateCalendarDto, UpdateEventDto,
};
use crate::common::errors::DomainError;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
/// Port for external calendar storage mechanisms
#[async_trait]
pub trait CalendarStoragePort: Send + Sync + 'static {
// Calendar operations
async fn create_calendar(
@@ -111,7 +109,6 @@ pub trait CalendarStoragePort: Send + Sync + 'static {
/// All methods require an explicit `user_id` parameter for authorization.
/// The CalDAV protocol handler extracts the user identity from JWT claims
/// and passes it through.
#[async_trait]
pub trait CalendarUseCase: Send + Sync + 'static {
// Calendar operations
async fn create_calendar(
-3
View File
@@ -7,11 +7,9 @@ use crate::application::dtos::contact_dto::{
GroupMembershipDto, UpdateContactDto, UpdateContactGroupDto,
};
use crate::common::errors::DomainError;
use async_trait::async_trait;
pub type CardDavRepositoryError = DomainError;
#[async_trait]
pub trait AddressBookUseCase: Send + Sync + 'static {
// Address Book operations
async fn create_address_book(
@@ -57,7 +55,6 @@ pub trait AddressBookUseCase: Send + Sync + 'static {
) -> Result<Vec<(String, bool)>, DomainError>;
}
#[async_trait]
pub trait ContactUseCase: Send + Sync + 'static {
// Contact operations
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError>;
@@ -5,7 +5,6 @@
//! the specific upload implementation (TUS-like protocol, S3 multipart, etc.).
use crate::common::errors::DomainError;
use async_trait::async_trait;
use bytes::Bytes;
use serde::Serialize;
use std::path::PathBuf;
@@ -53,7 +52,6 @@ pub struct UploadStatusResponseDto {
/// Implementations manage upload sessions, chunk storage, reassembly,
/// and cleanup, while the application layer only interacts through
/// this abstraction.
#[async_trait]
pub trait ChunkedUploadPort: Send + Sync + 'static {
/// Create a new upload session.
///
@@ -5,7 +5,6 @@
//! compression implementations (gzip, zstd, etc.).
use crate::common::errors::DomainError;
use async_trait::async_trait;
/// Compression level settings for file compression operations.
///
@@ -27,7 +26,6 @@ pub enum CompressionLevel {
/// Implementations of this trait provide the actual compression logic
/// (e.g., gzip, zstd) while the application layer remains agnostic
/// of the specific algorithm used.
#[async_trait]
pub trait CompressionPort: Send + Sync + 'static {
/// Compress data in memory.
async fn compress_data(
-2
View File
@@ -5,7 +5,6 @@
//! content-addressable storage implementation.
use crate::common::errors::DomainError;
use async_trait::async_trait;
use bytes::Bytes;
use futures::Stream;
use serde::Serialize;
@@ -92,7 +91,6 @@ pub struct DedupStatsDto {
/// Implementations store files by their content hash, eliminating
/// duplicate storage automatically. Multiple file references can
/// point to the same physical blob.
#[async_trait]
pub trait DedupPort: Send + Sync + 'static {
/// Store content with deduplication (from bytes).
///
-3
View File
@@ -1,9 +1,7 @@
use crate::application::dtos::favorites_dto::{BatchFavoritesResult, FavoriteItemDto};
use crate::common::errors::Result;
use async_trait::async_trait;
/// Defines operations for managing user favorites
#[async_trait]
pub trait FavoritesUseCase: Send + Sync {
/// Get all favorites for a user
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
@@ -40,7 +38,6 @@ pub trait FavoritesUseCase: Send + Sync {
/// Application services depend on this trait instead of
/// accessing `PgPool` directly. The concrete implementation
/// lives in `infrastructure::repositories::pg`.
#[async_trait]
pub trait FavoritesRepositoryPort: Send + Sync + 'static {
/// Gets all favorites for a user.
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
+6 -7
View File
@@ -1,4 +1,3 @@
use async_trait::async_trait;
use bytes::Bytes;
use futures::Stream;
use std::path::Path;
@@ -8,6 +7,9 @@ use std::sync::Arc;
use crate::application::dtos::file_dto::FileDto;
use crate::application::ports::storage_ports::CopyFolderTreeResult;
use crate::common::errors::DomainError;
use crate::application::services::file_management_service::FileManagementService;
use crate::application::services::file_retrieval_service::FileRetrievalService;
use crate::application::services::file_upload_service::FileUploadService;
// ─────────────────────────────────────────────────────
// Upload port
@@ -24,7 +26,6 @@ use crate::common::errors::DomainError;
/// - Chunked uploads: chunks already on disk → `upload_file_from_path`
/// - WebDAV PUT (new): handler streams to temp file → `update_file_streaming`
/// - WebDAV PUT (small/compat): `create_file` / `update_file` spool internally
#[async_trait]
pub trait FileUploadUseCase: Send + Sync + 'static {
/// Upload from a temp file already on disk (true streaming, ~256 KB RAM).
///
@@ -102,7 +103,6 @@ pub enum OptimizedFileContent {
}
/// Primary port for file retrieval operations
#[async_trait]
pub trait FileRetrievalUseCase: Send + Sync + 'static {
/// Gets a file by its ID
async fn get_file(&self, id: &str) -> Result<FileDto, DomainError>;
@@ -188,7 +188,6 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
// ─────────────────────────────────────────────────────
/// Primary port for file management operations
#[async_trait]
pub trait FileManagementUseCase: Send + Sync + 'static {
/// Moves a file to another folder
async fn move_file(
@@ -241,7 +240,7 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
/// Factory for creating file use case implementations
pub trait FileUseCaseFactory: Send + Sync + 'static {
fn create_file_upload_use_case(&self) -> Arc<dyn FileUploadUseCase>;
fn create_file_retrieval_use_case(&self) -> Arc<dyn FileRetrievalUseCase>;
fn create_file_management_use_case(&self) -> Arc<dyn FileManagementUseCase>;
fn create_file_upload_use_case(&self) -> Arc<FileUploadService>;
fn create_file_retrieval_use_case(&self) -> Arc<FileRetrievalService>;
fn create_file_management_use_case(&self) -> Arc<FileManagementService>;
}
-3
View File
@@ -1,4 +1,3 @@
use async_trait::async_trait;
use std::sync::Arc;
use crate::application::dtos::folder_dto::{
@@ -10,7 +9,6 @@ use crate::application::dtos::search_dto::{
use crate::common::errors::DomainError;
/// Primary port for folder operations
#[async_trait]
pub trait FolderUseCase: Send + Sync + 'static {
/// Creates a new folder
async fn create_folder(&self, dto: CreateFolderDto) -> Result<FolderDto, DomainError>;
@@ -89,7 +87,6 @@ pub trait FolderUseCase: Send + Sync + 'static {
* All search processing (filtering, scoring, sorting, categorization)
* is handled server-side in Rust for maximum efficiency.
*/
#[async_trait]
pub trait SearchUseCase: Send + Sync + 'static {
/// Performs a full search based on the specified criteria.
///
-2
View File
@@ -1,4 +1,3 @@
use async_trait::async_trait;
use std::path::PathBuf;
use crate::common::errors::DomainError;
@@ -10,7 +9,6 @@ pub use crate::domain::repositories::folder_repository::FolderRepository;
use super::storage_ports::{FileReadPort, FileWritePort};
/// Secondary port for storage operations
#[async_trait]
pub trait StoragePort: Send + Sync + 'static {
/// Resolves a domain path to a physical path
fn resolve_path(&self, storage_path: &StoragePath) -> PathBuf;
-3
View File
@@ -1,9 +1,7 @@
use crate::application::dtos::recent_dto::RecentItemDto;
use crate::common::errors::Result;
use async_trait::async_trait;
/// Defines operations for managing user recent items
#[async_trait]
pub trait RecentItemsUseCase: Send + Sync {
/// Get all recent items for a user
async fn get_recent_items(
@@ -36,7 +34,6 @@ pub trait RecentItemsUseCase: Send + Sync {
///
/// Abstracts access to the `auth.user_recent_files` table so that
/// `RecentService` does not depend directly on `PgPool`.
#[async_trait]
pub trait RecentItemsRepositoryPort: Send + Sync + 'static {
/// Gets the latest recent items for a user (ordered by date desc).
async fn get_recent_items(&self, user_id: &str, limit: i32) -> Result<Vec<RecentItemDto>>;
-3
View File
@@ -1,4 +1,3 @@
use async_trait::async_trait;
use crate::{
application::dtos::{
@@ -9,7 +8,6 @@ use crate::{
domain::entities::share::ShareItemType,
};
#[async_trait]
pub trait ShareUseCase: Send + Sync + 'static {
/// Create a new shared link for a file or folder
async fn create_shared_link(
@@ -60,7 +58,6 @@ pub trait ShareUseCase: Send + Sync + 'static {
async fn register_shared_link_access(&self, token: &str) -> Result<(), DomainError>;
}
#[async_trait]
pub trait ShareStoragePort: Send + Sync + 'static {
async fn save_share(
&self,
-8
View File
@@ -1,4 +1,3 @@
use async_trait::async_trait;
use bytes::Bytes;
use futures::Stream;
use serde_json::Value;
@@ -25,7 +24,6 @@ pub use crate::domain::repositories::folder_repository::FolderRepository;
///
/// Encapsulates every operation that queries state without modifying it:
/// get, list, content, stream, mmap, range, path resolution.
#[async_trait]
pub trait FileReadPort: Send + Sync + 'static {
/// Gets a file by its ID.
async fn get_file(&self, id: &str) -> Result<File, DomainError>;
@@ -199,7 +197,6 @@ pub struct CopyFolderTreeResult {
///
/// Covers: upload (buffered + streaming), move, delete, update,
/// and deferred registration for the write-behind cache.
#[async_trait]
pub trait FileWritePort: Send + Sync + 'static {
/// Streaming upload — saves a file from a temp file already on disk.
///
@@ -307,7 +304,6 @@ pub trait FileWritePort: Send + Sync + 'static {
// ─────────────────────────────────────────────────────
/// Secondary port for file path resolution
#[async_trait]
pub trait FilePathResolutionPort: Send + Sync + 'static {
/// Gets the storage path of a file
async fn get_file_path(&self, id: &str) -> Result<StoragePath, DomainError>;
@@ -317,7 +313,6 @@ pub trait FilePathResolutionPort: Send + Sync + 'static {
}
/// Secondary port for file/directory existence verification
#[async_trait]
pub trait StorageVerificationPort: Send + Sync + 'static {
/// Checks whether a file exists at the given path
async fn file_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError>;
@@ -327,14 +322,12 @@ pub trait StorageVerificationPort: Send + Sync + 'static {
}
/// Secondary port for directory management
#[async_trait]
pub trait DirectoryManagementPort: Send + Sync + 'static {
/// Creates directories if they do not exist
async fn ensure_directory(&self, storage_path: &StoragePath) -> Result<(), DomainError>;
}
/// Secondary port for storage usage management
#[async_trait]
pub trait StorageUsagePort: Send + Sync + 'static {
/// Updates storage usage statistics for a user
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError>;
@@ -362,7 +355,6 @@ pub trait StorageUsagePort: Send + Sync + 'static {
}
/// Generic storage service interface for calendar and contact services
#[async_trait]
pub trait StorageUseCase: Send + Sync + 'static {
/// Handle a request with the specified action and parameters
async fn handle_request(&self, action: &str, params: Value) -> Result<Value, DomainError>;
-2
View File
@@ -5,7 +5,6 @@
//! image processing implementations.
use crate::common::errors::DomainError;
use async_trait::async_trait;
use bytes::Bytes;
use std::path::{Path, PathBuf};
use std::sync::Arc;
@@ -63,7 +62,6 @@ pub struct ThumbnailStatsDto {
/// Implementations handle the actual image processing, caching,
/// and storage of thumbnails, while the application layer only
/// interacts through this abstraction.
#[async_trait]
pub trait ThumbnailPort: Send + Sync + 'static {
/// Check if a file is an image that can have thumbnails.
fn is_supported_image(&self, mime_type: &str) -> bool;
-2
View File
@@ -5,7 +5,6 @@
//! independent of specific image processing implementations.
use crate::common::errors::DomainError;
use async_trait::async_trait;
use bytes::Bytes;
/// Supported output formats for image transcoding.
@@ -74,7 +73,6 @@ pub struct TranscodeStatsDto {
/// Implementations handle the actual image conversion, caching,
/// and format detection, while the application layer only interacts
/// through this abstraction.
#[async_trait]
pub trait ImageTranscodePort: Send + Sync + 'static {
/// Check if a MIME type can be transcoded.
fn can_transcode(&self, mime_type: &str) -> bool;
-2
View File
@@ -1,10 +1,8 @@
use async_trait::async_trait;
use crate::application::dtos::trash_dto::TrashedItemDto;
use crate::common::errors::Result;
/// Port for trash-related use cases
#[async_trait]
pub trait TrashUseCase: Send + Sync {
/// List items in the user's trash
async fn get_trash_items(&self, user_id: &str) -> Result<Vec<TrashedItemDto>>;
-2
View File
@@ -5,14 +5,12 @@
//! implementation details.
use crate::common::errors::DomainError;
use async_trait::async_trait;
use tempfile::NamedTempFile;
/// Port for ZIP archive operations.
///
/// Implementations handle the actual ZIP file creation, compression,
/// and recursive folder traversal.
#[async_trait]
pub trait ZipPort: Send + Sync + 'static {
/// Create a ZIP archive containing the contents of a folder (recursively).
///