EntityStore Ecosystem
Overview
EntityStore is a comprehensive state management ecosystem for Flutter application development based on entity-centric design philosophy. It encapsulates business logic within immutable entities, provides abstracted data access through repository patterns, and achieves reactive UI synchronization.
๐ฏ Design Philosophy
EntityStore is designed based on three core principles:
- Entity-First Design: Architecture centered around business objects (entities) that form the core of the application
- Immutability Principle: State changes only through creation of new instances, preventing unexpected side effects
- Reactive Synchronization: Seamless development experience where data changes automatically reflect in the UI
๐ฆ Package Composition
The EntityStore ecosystem consists of three packages that can be selected based on your use case:
๐๏ธ entity_store (Core Package)
dependencies:
entity_store: ^6.0.0-dev.13
- Role: Foundation for entity-based state management
- Features: Entity abstraction, reactive UI synchronization, basic repository patterns
- Use Cases: In-memory state management, prototyping, testing environments
๐ฅ entity_store_firestore
dependencies:
entity_store_firestore: ^6.0.0-dev.15
- Role: Integration with Firebase Firestore
- Features: Cloud synchronization, real-time updates, offline support, transaction processing
- Use Cases: Multi-device synchronization, real-time collaboration, scalable web apps
๐พ entity_store_sembast
dependencies:
entity_store_sembast: ^6.0.0-dev.13
- Role: Integration with Sembast (NoSQL local database)
- Features: High-performance local storage, complex queries, data encryption support
- Use Cases: Offline-first apps, high-speed local processing, privacy-focused applications
โจ Key Benefits
๐ Enhanced Development Efficiency
// Simple entity operations
final todo = context.watchOne<int, Todo>(todoId)!;
return CheckboxListTile(
title: Text(todo.name),
value: todo.isDone,
onChanged: (value) => todoRepository.save(todo.toggle()),
);
๐ Automatic UI Synchronization
Entity changes are automatically reflected throughout the UI. No need to write complex state management code.
๐๏ธ Flexible Architecture
// Development: In-memory
final repository = TodoRepository(controller, InMemoryStorageHandler());
// Production: Firestore
final repository = TodoFirestoreRepository(controller, firestore);
// Local-focused: Sembast
final repository = TodoSembastRepository(controller, database);
๐ Type Safety
TypeScript-like type safety allows compile-time error detection.
๐ฏ Usage Example: Todo Application
Entity Definition
class Todo implements Entity<int> {
@override
final int id;
final String name;
final bool isDone;
final DateTime createdAt;
const Todo({
required this.id,
required this.name,
required this.isDone,
required this.createdAt,
});
// Create a new Todo
factory Todo.create(int id, String name) {
return Todo(
id: id,
name: name,
isDone: false,
createdAt: DateTime.now(),
);
}
// Toggle completion status
Todo toggle() => Todo(
id: id,
name: name,
isDone: !isDone,
createdAt: createdAt,
);
// Update name
Todo rename(String newName) => Todo(
id: id,
name: newName,
isDone: isDone,
createdAt: createdAt,
);
}
Repository Implementation (Firestore Version)
class TodoFirestoreRepository extends FirestoreRepository<int, Todo> {
TodoFirestoreRepository(super.controller, super.instance);
@override
String get collectionId => 'todos';
@override
String idToString(int id) => id.toString();
@override
Todo fromJson(Map<String, dynamic> json) {
return Todo(
id: json['id'] as int,
name: json['name'] as String,
isDone: json['isDone'] as bool,
createdAt: DateTime.parse(json['createdAt'] as String),
);
}
@override
Map<String, dynamic> toJson(Todo entity) {
return {
'id': entity.id,
'name': entity.name,
'isDone': entity.isDone,
'createdAt': entity.createdAt.toIso8601String(),
};
}
}
UI Implementation
class TodoList extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Watch all todos
final todos = context.watchAll<int, Todo>();
return ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos.values.elementAt(index);
return TodoTile(todoId: todo.id);
},
);
}
}
class TodoTile extends StatelessWidget {
final int todoId;
const TodoTile({required this.todoId});
@override
Widget build(BuildContext context) {
// Watch specific todo
final todo = context.watchOne<int, Todo>(todoId);
if (todo == null) return const SizedBox.shrink();
return ListTile(
title: Text(todo.name),
leading: Checkbox(
value: todo.isDone,
onChanged: (_) {
// Entity update (automatic UI sync)
todoRepository.save(todo.toggle());
},
),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
// Entity deletion (automatic UI update)
todoRepository.deleteById(todo.id);
},
),
);
}
}
Application Setup
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return EntityStoreProviderScope(
entityStoreNotifier: entityStoreNotifier,
child: MaterialApp(
title: 'EntityStore Todo',
home: TodoHomePage(),
),
);
}
}
๐จ Advanced Features
Reactive Queries
// Watch only incomplete todos
final activeTodos = context.watchAll<int, Todo>(
(todo) => !todo.isDone,
);
// Watch count of completed todos
final completedCount = context.selectAll<int, Todo, int>(
(todos) => todos.values.where((todo) => todo.isDone).length,
);
Efficient Data Fetching
// Pagination support
final todos = await todoRepository
.query()
.orderBy('createdAt', descending: true)
.limit(20)
.findAll();
// Conditional search
final urgentTodos = await todoRepository
.query()
.where('priority', isEqualTo: 'high')
.where('isDone', isEqualTo: false)
.findAll();
Transaction Processing (Firestore)
await todoRepository.transaction((transaction) async {
final todo = await transaction.findById(todoId);
if (todo != null) {
await transaction.save(todo.toggle());
await transaction.save(createLogEntry(todo));
}
});
๐ง Getting Started
1. Package Selection and Installation
dependencies:
flutter:
sdk: flutter
# Core package (required)
entity_store: ^6.0.0-dev.13
# Choose based on your needs
entity_store_firestore: ^6.0.0-dev.15 # Cloud sync
entity_store_sembast: ^6.0.0-dev.13 # Local DB
2. Basic Setup
// Initialize entity store
final entityStoreNotifier = EntityStoreNotifier();
final entityStoreController = EntityStoreController(entityStoreNotifier);
// Repository setup (example: Firestore)
final firestore = FirebaseFirestore.instance;
final todoRepository = TodoFirestoreRepository(
entityStoreController,
firestore,
);
3. Dependency Injection (Recommended)
// Example using Riverpod
final entityStoreProvider = Provider((ref) => EntityStoreNotifier());
final entityStoreControllerProvider = Provider((ref) =>
EntityStoreController(ref.watch(entityStoreProvider)));
final todoRepositoryProvider = Provider((ref) =>
TodoFirestoreRepository(
ref.watch(entityStoreControllerProvider),
FirebaseFirestore.instance,
));
๐ Package Selection Guide
Requirements | Recommended Package | Reasons |
---|---|---|
๐งช Prototyping & Learning | entity_store |
Simple, lightweight, low learning curve |
๐ Real-time Collaboration | entity_store_firestore |
Auto-sync, scalability |
๐ฑ Offline-first Apps | entity_store_sembast |
Fast, offline support, privacy |
๐ข Enterprise Apps | Combined usage | Hybrid configuration as needed |
๐ Next Steps
- Quick Start Guide - Experience EntityStore in 5 minutes
- Architecture Guide - Detailed explanation of design philosophy
- Best Practices - Practical development know-how
- Migration Guide - Migration from existing projects
๐ค Community
- GitHub: soraef/entity_store
- Discord: EntityStore Community
- Issues: Bug reports & feature requests at GitHub Issues
๐ License
This project is released under the MIT License.