Dependency Injection
Zuraffa v3 provides automated dependency injection setup using get_it. The --di flag generates registration files for repositories, data sources, and services.
Overview
Dependency injection in Zuraffa follows these principles:
- Infrastructure only: DI registration focuses on infrastructure components (repositories, datasources)
- Manual UseCase registration: UseCases are instantiated by Presenters using
registerUseCase() - Clean separation: Presentation layer components are instantiated directly in Views
Generated Files
When using --di, Zuraffa generates registration files for each component:
lib/src/di/
├── datasources/
│ ├── product_remote_data_source_di.dart
│ └── product_local_data_source_di.dart
├── repositories/
│ └── product_repository_di.dart
└── index.dart
Basic Usage
1. Generate with DI
zfa generate Product \
--methods=get,getList,create,update,delete \
--data \
--di
This generates:
- Repository registration
- DataSource registration (both remote and local if using
--cache)
2. Register Dependencies
// main.dart
import 'package:get_it/get_it.dart';
import 'package:zuraffa/zuraffa.dart';
import 'src/di/index.dart'; // Auto-generated
final getIt = GetIt.instance;
void main() async {
// Enable Zuraffa logging (optional)
Zuraffa.enableLogging();
// Register all dependencies
await setupDependencies(getIt);
runApp(MyApp());
}
3. Use in Presentation Layer
// In your View
class ProductView extends CleanView {
const ProductView();
State<ProductView> createState() => _ProductViewState(
ProductController(
ProductPresenter(
productRepository: getIt<ProductRepository>(),
),
),
);
}
Caching with DI
When using --cache, Zuraffa generates dual DataSource registration:
zfa generate Product \
--methods=get,getList \
--data \
--cache \
--di
This generates:
- Remote DataSource registration
- Local DataSource registration
- Cached Repository registration
Using Mock DataSources
Use --use-mock to register mock datasources instead of remote:
zfa generate Product \
--methods=get,getList \
--data \
--di \
--use-mock
This registers ProductMockDataSource instead of ProductRemoteDataSource for development without backend.
Generated Registration Code
Repository Registration
// lib/src/di/repositories/product_repository_di.dart
import 'package:get_it/get_it.dart';
import '../../data/repositories/data_product_repository.dart';
import '../../data/data_sources/product/product_remote_data_source.dart';
import '../../domain/repositories/product_repository.dart';
Future<void> registerProductRepository(GetIt getIt) async {
getIt.registerLazySingleton<ProductRepository>(() {
return DataProductRepository(
getIt<ProductRemoteDataSource>(),
);
});
}
DataSource Registration
// lib/src/di/datasources/product_remote_data_source_di.dart
import 'package:get_it/get_it.dart';
import '../../data/data_sources/product/product_remote_data_source.dart';
Future<void> registerProductRemoteDataSource(GetIt getIt) async {
getIt.registerLazySingleton<ProductRemoteDataSource>(() {
return ProductRemoteDataSource();
});
}
Combined Setup
// lib/src/di/index.dart
import 'package:get_it/get_it.dart';
import 'repositories/product_repository_di.dart';
import 'datasources/product_remote_data_source_di.dart';
Future<void> setupDependencies(GetIt getIt) async {
// Register data sources first
await registerProductRemoteDataSource(getIt);
// Then register repositories
await registerProductRepository(getIt);
}
ZFA Patterns and DI
Entity-Based Pattern
For entity-based generation, DI handles the complete stack:
zfa generate Product \
--methods=get,getList,create,update,delete \
--data \
--vpc \
--di
Single Repository Pattern
For custom UseCases with single repository:
zfa generate ProcessCheckout \
--domain=checkout \
--repo=Checkout \
--params=CheckoutRequest \
--returns=OrderConfirmation \
--di
This generates only repository registration, as UseCases are handled by Presenters.
Orchestrator Pattern
For orchestrator UseCases:
zfa generate ProcessCheckout \
--domain=checkout \
--usecases=ValidateCart,CreateOrder,ProcessPayment \
--params=CheckoutRequest \
--returns=Order \
--di
This generates registration for the repositories of the composed UseCases.
Manual Registration
Some components require manual registration:
UseCases
UseCases are registered by Presenters using registerUseCase():
class ProductPresenter extends Presenter {
final ProductRepository productRepository;
late final GetProductUseCase _getProduct;
late final GetProductListUseCase _getProductList;
ProductPresenter({required this.productRepository}) {
// Use registerUseCase to properly dispose when Presenter is disposed
_getProduct = registerUseCase(GetProductUseCase(productRepository));
_getProductList = registerUseCase(GetProductListUseCase(productRepository));
}
}
Presentation Layer
Presentation layer components are instantiated directly in Views:
class ProductView extends CleanView {
const ProductView();
State<ProductView> createState() => _ProductViewState(
ProductController(
ProductPresenter(
productRepository: getIt<ProductRepository>(),
),
),
);
}
Advanced Configuration
Conditional Registration
Register different implementations based on environment:
Future<void> setupDependencies(GetIt getIt) async {
if (kDebugMode) {
// Use mock data in debug mode
await registerProductMockDataSource(getIt);
} else {
// Use real API in release mode
await registerProductRemoteDataSource(getIt);
}
await registerProductRepository(getIt);
}
Using with --use-mock
For development without backend:
zfa generate Product \
--methods=get,getList \
--data \
--di \
--mock \
--use-mock
This generates mock datasources and registers them instead of remote ones.
Best Practices
1. Use --di with Complete Stacks
# Good: Complete architecture with DI
zfa generate Product \
--methods=get,getList,create,update,delete \
--data \
--di
# Good: With caching
zfa generate Product \
--methods=get,getList \
--data \
--cache \
--di
2. Combine with Mock Data for Development
# Development setup with mock data
zfa generate Product \
--methods=get,getList,create,update,delete \
--data \
--di \
--mock \
--use-mock
3. Use Domain Organization
# Register domain-specific dependencies
zfa generate ProcessCheckout \
--domain=checkout \
--repo=Checkout \
--di
4. Handle Caching Properly
# Complete caching setup with DI
zfa generate Product \
--methods=get,getList \
--data \
--cache \
--cache-storage=hive \
--di
Migration from 1.x
Before (1.x)
# Generated DI for all components including UseCases and Presenters
zfa generate Product --di
After (2.0.0)
# DI only for infrastructure components (repositories, datasources)
zfa generate Product --di
# UseCases handled by Presenters with registerUseCase()
# Presenters/Controllers instantiated in Views
Troubleshooting
Circular Dependencies
If you encounter circular dependency errors, ensure proper registration order:
- Register DataSources first
- Then register Repositories
- UseCases are handled by Presenters
Missing Registrations
If getting "Unregistered type" errors, ensure you've called setupDependencies() in your main function.
Next Steps
- Caching - Dual datasource caching setup
- Mock Data - Development with mock data
- CLI Reference - Complete DI flag documentation