# Flow-DI
[](https://www.rust-lang.org)
[](https://crates.io/crates/flow-di)
[](https://docs.rs/flow-di)
[](https://opensource.org/licenses/MIT)
A Rust dependency injection framework inspired by C# AutoFac and Microsoft.Extensions.DependencyInjection.
[中文文档](README_CN.md) | English
## Features
- **Three Lifetime Management Types**: Singleton, Scoped, Transient
- **Keyed Service Support**: Named services and key-based service resolution
- **Automatic Dependency Injection**: Automatic dependency injection when creating service instances
- **Fluent API**: Easy-to-use service registration API using builder pattern
- **Circular Dependency Detection**: Automatic detection and prevention of circular dependencies
- **Thread Safety**: Full support for multi-threaded environments
- **Scope Management**: Nested scopes and automatic resource cleanup
- **Object Safety**: Core traits are dyn-compatible for flexible usage patterns
## Quick Start
Add this to your `Cargo.toml`:
```toml
[dependencies]
flow-di = "0.1.0"
```
### Basic Usage
```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
use std::sync::Arc;
// Define service interface
trait IRepository: Send + Sync {
fn get_data(&self) -> String;
}
// Implement service
struct DatabaseRepository {
connection_string: String,
}
impl IRepository for DatabaseRepository {
fn get_data(&self) -> String {
format!("Data from database: {}", self.connection_string)
}
}
fn main() {
// Configure and build container
let provider = ContainerBuilder::new()
.add_instance("localhost:5432".to_string()) // Register configuration
.add_singleton_with_deps::<DatabaseRepository, String>(
|connection_string| DatabaseRepository {
connection_string: (*connection_string).clone(),
}
)
.build();
// Resolve service
let repository = provider.get_required_service::<DatabaseRepository>().unwrap();
println!("{}", repository.get_data());
}
```
### Lifetime Management
#### Singleton
Services registered as singleton will have only one instance throughout the application lifetime:
```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
let provider = ContainerBuilder::new()
.add_singleton_simple::<String, String>(|| "singleton instance".to_string())
.build();
let instance1 = provider.get_required_service::<String>().unwrap();
let instance2 = provider.get_required_service::<String>().unwrap();
// instance1 and instance2 are the same instance
assert!(Arc::ptr_eq(&instance1, &instance2));
```
#### Scoped
Services registered as scoped will share the same instance within the same scope:
```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
let provider = ContainerBuilder::new()
.add_scoped_simple::<String, String>(|| "scoped instance".to_string())
.build();
let mut scope = provider.create_scope().unwrap();
let service1 = scope.get_required_service::<String>().unwrap();
let service2 = scope.get_required_service::<String>().unwrap();
// service1 and service2 are the same instance within the same scope
assert!(Arc::ptr_eq(&service1, &service2));
```
#### Transient
Services registered as transient will create a new instance for each request:
```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
let provider = ContainerBuilder::new()
.add_transient_simple::<String, String>(|| "transient instance".to_string())
.build();
let instance1 = provider.get_required_service::<String>().unwrap();
let instance2 = provider.get_required_service::<String>().unwrap();
// Each time is a new instance
assert!(!Arc::ptr_eq(&instance1, &instance2));
```
### Named Services
You can register multiple implementations of the same interface using different names:
```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
// Register named services
let provider = ContainerBuilder::new()
.add_named_singleton_simple::<String, String>("primary", || "primary service".to_string())
.add_named_singleton_simple::<String, String>("secondary", || "secondary service".to_string())
.build();
// Resolve named services
let primary = provider.get_required_keyed_service::<String>("primary").unwrap();
let secondary = provider.get_required_keyed_service::<String>("secondary").unwrap();
assert_eq!(*primary, "primary service");
assert_eq!(*secondary, "secondary service");
```
### Dependency Injection
Services can depend on other services, and dependencies will be automatically injected:
```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
use std::sync::Arc;
trait ILogger: Send + Sync {
fn log(&self, message: &str);
}
trait IRepository: Send + Sync {
fn get_user(&self, id: i32) -> String;
}
struct ConsoleLogger;
impl ILogger for ConsoleLogger {
fn log(&self, message: &str) {
println!("Log: {}", message);
}
}
struct UserRepository {
logger: Arc<ConsoleLogger>,
}
impl IRepository for UserRepository {
fn get_user(&self, id: i32) -> String {
self.logger.log(&format!("Getting user {}", id));
format!("User {}", id)
}
}
struct UserService {
repository: Arc<UserRepository>,
logger: Arc<ConsoleLogger>,
}
impl UserService {
fn new(repository: Arc<UserRepository>, logger: Arc<ConsoleLogger>) -> Self {
Self { repository, logger }
}
fn get_user(&self, id: i32) -> String {
self.logger.log("UserService::get_user called");
self.repository.get_user(id)
}
}
let provider = ContainerBuilder::new()
// Register logger
.add_singleton_simple::<ConsoleLogger, ConsoleLogger>(|| ConsoleLogger)
// Register repository with logger dependency
.add_scoped_with_deps::<UserRepository, UserRepository, ConsoleLogger>(
|logger| UserRepository { logger }
)
// Register service with multiple dependencies
.add_transient_with_deps2::<UserService, UserService, UserRepository, ConsoleLogger>(
|repository, logger| UserService::new(repository, logger)
)
.build();
let mut scope = provider.create_scope().unwrap();
let user_service = scope.get_required_service::<UserService>().unwrap();
let user = user_service.get_user(123);
println!("{}", user); // Output: User 123
```
### Service Scopes
Service scopes provide isolation for scoped services and automatic resource cleanup:
```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
let provider = ContainerBuilder::new()
.add_scoped_simple::<String, String>(|| "scoped service".to_string())
.build();
// Create scope
let mut scope = provider.create_scope().unwrap();
// ServiceScope also implements ServiceProvider, so it can be used to resolve services
let service = scope.get_required_service::<String>().unwrap();
println!("{}", service);
// Release scope
scope.dispose();
// Check if disposed
let is_disposed = scope.is_disposed();
assert!(is_disposed);
```
## API Reference
### ContainerBuilder
The `ContainerBuilder` provides a fluent API for registering services:
#### Basic Registration Methods
- `add_singleton<TService, TImplementation>(factory)` - Register singleton service
- `add_scoped<TService, TImplementation>(factory)` - Register scoped service
- `add_transient<TService, TImplementation>(factory)` - Register transient service
- `add_instance<T>(instance)` - Register singleton instance
#### Simple Registration Methods (No Dependencies)
- `add_singleton_simple<TService, TImplementation>(factory)` - Register singleton service without dependencies
- `add_scoped_simple<TService, TImplementation>(factory)` - Register scoped service without dependencies
- `add_transient_simple<TService, TImplementation>(factory)` - Register transient service without dependencies
#### Dependency Injection Methods
- `add_singleton_with_deps<TService, TImplementation, TDep>(factory)` - Register singleton with one dependency
- `add_singleton_with_deps2<TService, TImplementation, TDep1, TDep2>(factory)` - Register singleton with two dependencies
- Similar methods for `scoped` and `transient`
#### Named Service Methods
- `add_named_singleton<TService, TImplementation>(name, factory)` - Register named singleton
- `add_named_scoped<TService, TImplementation>(name, factory)` - Register named scoped service
- `add_named_transient<TService, TImplementation>(name, factory)` - Register named transient service
- `add_named_instance<T>(name, instance)` - Register named singleton instance
### ServiceProvider
The `ServiceProvider` provides methods for resolving services:
#### Core Methods (Object-Safe)
- `get_service_raw(&self, key: &ServiceKey)` - Get raw service as `Arc<dyn Any>`
#### Extension Methods (via ServiceProviderExt trait)
- `get_service<T>()` - Get optional service of type T
- `get_required_service<T>()` - Get required service of type T (panics if not found)
- `get_keyed_service<T>(key)` - Get optional named service
- `get_required_keyed_service<T>(key)` - Get required named service
- `create_scope()` - Create a new service scope
### ServiceScope
Service scopes provide isolated contexts for scoped services:
- Implements `ServiceProviderExt` trait
- Automatic resource cleanup on disposal
- Thread-safe operations
- Nested scope support
## Architecture
### Object Safety
Flow-DI is designed with object safety in mind:
- `ServiceProvider` trait is object-safe and can be used as `dyn ServiceProvider`
- `ServiceProviderExt` provides generic methods via extension trait pattern
- Automatic implementation for all `ServiceProvider` implementors
### Thread Safety
All components are thread-safe:
- Services can be resolved from multiple threads
- Scopes can be created and managed concurrently
- Singleton instances are safely shared across threads
### Memory Management
- Uses `Arc<T>` for service instances to enable safe sharing
- Automatic cleanup of scoped services when scopes are disposed
- No memory leaks from circular dependencies (detection prevents them)
## Examples
See the [examples](examples/) directory for complete examples:
- [Basic Usage](examples/basic_usage.rs) - Comprehensive example showing all features
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Acknowledgments
- Inspired by [AutoFac](https://autofac.org/) for .NET
- Inspired by [Microsoft.Extensions.DependencyInjection](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection)