Philosophy¶
Introduction¶
Bond's philosophy is rooted in the belief that mobile app development should be productive, enjoyable, and focused on solving real business problems rather than wrestling with infrastructure concerns. Every design decision in Bond reflects our commitment to developer experience, code quality, and long-term maintainability.
Core Principles¶
Convention over Configuration¶
Bond provides sensible defaults and established patterns that work for the majority of use cases. While you can customize and override these conventions when needed, the default path should be the right path for most developers.
Example: When you create a new feature with bond create feature auth, Bond generates a complete feature structure with Service Provider, API service, repository, and form controllers following established patterns. You don't need to decide how to organize these files or wire them together - Bond provides a proven structure.
// Bond generates this structure automatically
features/
auth/
auth_service_provider.dart
data/
repositories/
auth_repository.dart
api/
auth_api_service.dart
presentation/
controllers/
login_form_controller.dart
pages/
login_page.dart
Explicit Dependencies¶
Bond makes all dependencies explicit through the Service Provider pattern and dependency injection. There are no hidden global variables, singletons, or magic - every dependency is clearly declared and can be easily swapped for testing or different implementations.
Why this matters: Explicit dependencies make code easier to understand, test, and maintain. When you see a class constructor, you immediately know what it depends on.
// ✅ Explicit dependencies - clear what this class needs
class UserRepository {
final UserApiService apiService;
final TokenStorage tokenStorage;
final UserCache cache;
UserRepository({
required this.apiService,
required this.tokenStorage,
required this.cache,
});
}
// ❌ Hidden dependencies - unclear what this class uses
class UserRepository {
void getUser() {
final api = GlobalApiClient.instance; // Hidden dependency!
final token = GlobalAuth.currentToken; // Another hidden dependency!
}
}
Typed Everything¶
Bond embraces Dart's type system to catch errors at compile time rather than runtime. From API responses to form validation, Bond uses types to provide better IDE support, catch bugs early, and make code self-documenting.
Example: BondFire's typed responses ensure you always know what data structure you're working with:
// The type system tells you exactly what you'll get
final response = await bondFire.get<ListResponse<User>>('/users')
.factory(ListResponse<User>.fromJson)
.execute();
// IDE knows response.data is List<User>
for (final user in response.data) {
print(user.name); // Type-safe access
}
Developer Experience First¶
Every API, error message, and tool in Bond is designed with developer experience in mind. This means:
- Clear Error Messages: When something goes wrong, Bond tells you exactly what happened and how to fix it
- Comprehensive Documentation: Every feature is documented with examples and common use cases
- Helpful CLI Tools: The Bond CLI guides you through setup and generation with interactive prompts
- IDE Integration: Bond works seamlessly with VS Code, Android Studio, and other Flutter IDEs
Testability by Design¶
Bond's architecture makes testing natural and straightforward. The Service Provider pattern, dependency injection, and clear separation of concerns mean you can easily test individual components in isolation.
// Easy to test - just provide mock dependencies
void main() {
group('UserRepository', () {
late UserRepository repository;
late MockUserApiService mockApi;
late MockTokenStorage mockStorage;
setUp(() {
mockApi = MockUserApiService();
mockStorage = MockTokenStorage();
repository = UserRepository(
apiService: mockApi,
tokenStorage: mockStorage,
cache: InMemoryUserCache(),
);
});
test('should return user when API call succeeds', () async {
// Test implementation
});
});
}
Architectural Decisions¶
Service Provider Pattern¶
Bond uses the Service Provider pattern (inspired by Laravel) to organize dependency registration and feature bootstrapping. This pattern provides several benefits:
- Clear Organization: Each feature has its own provider that registers all related services
- Lazy Loading: Services are only created when needed
- Easy Testing: Providers can be swapped with test implementations
- Modular Architecture: Features can be developed and tested independently
Feature-Based Structure¶
Rather than organizing code by technical layers (models, views, controllers), Bond organizes by business features (auth, posts, notifications). This approach:
- Makes it easier to find related code
- Enables teams to work on different features independently
- Simplifies testing and deployment of individual features
- Reduces merge conflicts in large teams
Shared Decoding Pipeline¶
Bond uses a shared response decoding system that works across networking and caching. This means you define your JSON-to-model conversion once, and it works everywhere:
class UserServiceProvider extends ServiceProvider with ResponseDecoding {
@override
Map<Type, JsonFactory> get factories => {
User: (json) => User.fromJson(json),
};
}
// This factory is used automatically by:
// - BondFire for API responses
// - Bond Cache for cached objects
// - Any other Bond package that needs JSON conversion
Design Trade-offs¶
Every framework makes trade-offs. Bond is no exception. Here are the conscious decisions we've made and why:
Opinionated Structure vs. Flexibility¶
Trade-off: Bond provides an opinionated project structure and conventions.
Why: While this reduces flexibility, it eliminates decision fatigue and ensures consistency across teams and projects. The structure is based on years of Flutter development experience and proven patterns.
Escape Hatch: You can always customize or override Bond's conventions when needed. The framework doesn't lock you in.
Bundle Size vs. Features¶
Trade-off: Bond includes many packages and features out of the box.
Why: The convenience of having everything work together seamlessly outweighs the small increase in bundle size. Modern app stores and devices handle larger apps well.
Mitigation: Bond packages are modular - you can use only what you need. Tree shaking eliminates unused code in release builds.
Learning Curve vs. Power¶
Trade-off: Bond has concepts like Service Providers that developers need to learn.
Why: These patterns provide significant long-term benefits in terms of maintainability, testability, and team productivity. The upfront learning investment pays dividends over time.
Support: Comprehensive documentation, examples, and CLI tools help developers learn Bond concepts quickly.
Influences and Inspiration¶
Bond draws inspiration from several successful frameworks and patterns:
Laravel (PHP)¶
- Service Provider pattern for dependency registration
- Comprehensive documentation approach
- Developer-friendly CLI tools
- Convention over configuration philosophy
Spring Boot (Java)¶
- Dependency injection and inversion of control
- Auto-configuration and sensible defaults
- Modular architecture with clear boundaries
Ruby on Rails¶
- Convention over configuration
- Developer happiness as a primary goal
- Comprehensive tooling and generators
Flutter/Dart Ecosystem¶
- Embracing Dart's type system
- Integration with existing Flutter patterns (Riverpod, GetIt, etc.)
- Respect for Flutter's widget-based architecture
Evolution and Future¶
Bond's philosophy continues to evolve based on community feedback and real-world usage. Key areas of ongoing development include:
Performance Optimization¶
Continuously improving the performance characteristics of Bond packages while maintaining developer experience.
Ecosystem Integration¶
Better integration with popular Flutter packages and tools while maintaining Bond's cohesive experience.
Developer Tooling¶
Enhanced CLI tools, IDE plugins, and debugging support to make Bond development even more productive.
Community Patterns¶
Incorporating proven patterns and solutions from the Bond community back into the core framework.
Conclusion¶
Bond's philosophy centers on making Flutter development more productive and enjoyable by providing proven patterns, eliminating boilerplate, and focusing on developer experience. While this requires some upfront learning, the long-term benefits in terms of code quality, team productivity, and maintainability make it worthwhile.
The framework is designed to grow with your needs - from simple prototypes to complex enterprise applications. By following Bond's conventions and patterns, you're building on a foundation that has been tested in real-world applications and refined based on community feedback.
Next Steps¶
Now that you understand Bond's philosophy and design principles:
- Learn about Upgrades - Understand how Bond evolves over time
- Get Started - Create your first Bond application
- Explore Core Concepts - Deep dive into Service Providers and architecture