virnavi_isar_e2m 0.0.1
virnavi_isar_e2m: ^0.0.1 copied to clipboard
A generic Isar DAO wrapper** that bridges the gap between Isar entities and domain models, streamlining NoSQL database interactions in Flutter applications.
π οΈ virnavi_isar_e2m #
A generic Isar DAO wrapper that bridges the gap between Isar entities and domain models, streamlining NoSQL database interactions in Flutter applications.
This package provides a robust abstraction layer over Isar, offering generic DAOs with built-in support for Optional and ModelStream from the virnavi_common_sdk. It handles the boilerplate of transaction management, entity-to-model conversion, and reactive data streaming.
β¨ Features #
β
BaseNoSqlDaoImpl β Generic implementation of CRUD operations (Get, Upsert, Delete)
β
Reactive Streams β Automatic conversion of Isar watchers to ModelStream
β
Entity Abstractions β BaseNoSqlEntity and BaseNoSqlSingletonEntity for consistent schema
β
Transaction Management β Safe read/write transaction handling
β
Model-Entity Mapping β Enforced separation of concerns between database entities and domain models
π Usage #
1. Define your Entity #
Extend BaseNoSqlEntity to create your Isar collection. The base class automatically handles tempId and updatedDateTime.
part of 'entities.dart';
@Collection(accessor: 'users')
class UserEntity extends BaseNoSqlEntity {
@Index(unique: true, replace: true)
String id = '';
String name = '';
int? age;
String json = '';
UserEntity();
factory UserEntity.empty() => UserEntity();
factory UserEntity.fromModel(UserModel model) {
final entity = UserEntity();
entity.id = model.id;
entity.name = model.name;
entity.age = model.age;
entity.json = jsonEncode(model.toJson());
return entity;
}
UserModel toModel() => UserModel.fromJson(jsonDecode(json));
}
2. Define your Model #
Create a pure Dart model for your domain layer with JSON serialization support. This ensures your business logic isn't tied directly to the database implementation.
part of 'models.dart';
@JsonSerializable(explicitToJson: true)
class UserModel {
final String id;
final String name;
final int? age;
UserModel({
required this.id,
required this.name,
this.age,
});
factory UserModel.empty() => UserModel(
id: '',
name: '',
age: null,
);
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
Map<String, dynamic> toJson() => _$UserModelToJson(this);
}
3. Create the DAO Interface #
Define an abstract DAO interface for your domain layer.
import 'package:virnavi_isar_e2m/virnavi_isar_e2m.dart';
abstract class UserDao extends BaseNoSqlDao<UserModel, String> {}
4. Implement the DAO #
Extend BaseNoSqlDaoImpl to connect your entity and model. The conversion logic leverages the entity's built-in methods.
import 'package:virnavi_common_sdk/virnavi_common_sdk.dart';
import 'package:virnavi_isar_e2m/virnavi_isar_e2m.dart';
import 'package:isar_community/isar.dart';
import 'package:injectable/injectable.dart';
@Singleton(as: UserDao)
class UserDaoImpl extends BaseNoSqlDaoImpl<UserModel, UserEntity, String>
implements UserDao {
UserDaoImpl();
@override
IsarCollection<UserEntity> get entityCollection => Database.shared.db.users;
@override
Optional<UserEntity> convertToEntity(UserModel? model) {
if (model == null) return Optional.empty();
return Optional.ofNullable(UserEntity.fromModel(model));
}
@override
Optional<UserModel> convertToModel(UserEntity? entity) =>
Optional.ofNullable(entity?.toModel());
@override
QueryBuilder<UserEntity, UserEntity, QAfterWhereClause> idEqual(
QueryBuilder<UserEntity, UserEntity, QWhereClause> queryBuilder,
String value) =>
queryBuilder.idEqualTo(value);
@override
String idFromModel(UserModel model) => model.id;
}
5. Use it in your App #
void main() async {
// Initialize Isar and register DAOs with dependency injection
// (using get_it or injectable)
final userDao = getIt<UserDao>();
// Upsert a user
final user = UserModel(id: '1', name: 'Shakib', age: 30);
await userDao.upsert(user);
// Get by ID
final userOpt = await userDao.getById('1');
userOpt.ifPresent((u) => print('Found: ${u.name}'));
// Watch changes (Stream) - automatically converted to ModelStream
final stream = await userDao.getByIdWatcher('1');
stream.listen((opt) {
opt.ifPresent((u) => print('Updated name: ${u.name}'));
});
// Get all users
final allUsers = await userDao.getAll();
print('Total users: ${allUsers.length}');
// Watch all users
final allStream = await userDao.getAllWatcher();
allStream.listen((opt) {
opt.ifPresent((users) => print('User count: ${users.length}'));
});
}
6. Singleton Entities #
For storing configuration or single-instance data, use BaseNoSqlSingletonEntity.
part of 'entities.dart';
@Collection(accessor: 'appConfig')
class ConfigEntity extends BaseNoSqlSingletonEntity {
String themeMode = 'light';
String language = 'en';
String json = '';
ConfigEntity();
factory ConfigEntity.empty() => ConfigEntity();
factory ConfigEntity.fromModel(ConfigModel model) {
final entity = ConfigEntity();
entity.themeMode = model.themeMode;
entity.language = model.language;
entity.json = jsonEncode(model.toJson());
return entity;
}
ConfigModel toModel() => ConfigModel.fromJson(jsonDecode(json));
}
π§ How It Works #
-
Clean Architecture: The package enforces separation of concerns:
Entityclasses handle Isar persistence withtempIdandupdatedDateTimemanaged automaticallyModelclasses represent your domain objects with business logic- JSON serialization in entities provides a flexible backup for complex nested objects
-
Generic CRUD Operations:
BaseNoSqlDaoImplprovides out-of-the-box implementations:- Read:
getById,getAll,getIdList - Watch:
getByIdWatcher,getAllWatcher,getIdListWatcher(reactive streams) - Write:
upsert,upsertAll - Delete:
delete,deleteAll,clear
- Read:
-
Reactive Streams: All watcher methods return
ModelStreamthat automatically:- Convert Isar entity streams to domain model streams
- Wrap results in
Optionalfor safe null handling - Fire immediately with current data
-
Type Safety: Uses
Optionalfromvirnavi_common_sdkto:- Handle nullability explicitly
- Prevent runtime null errors
- Provide functional programming patterns (
ifPresent,orElse, etc.)
-
Transaction Management: All write operations are wrapped in Isar transactions automatically, ensuring data consistency.
π§° Dependencies #
π§βπ» Contributors #
- Mohammed Shakib (@shakib1989) - Main Library Development
- Shuvo Prosad Sarnakar (@shuvoprosadsarnakar) - Extensive documentation and getting the project for pub.flutter-io.cn.
πͺͺ License #
This project is licensed under the MIT License β see the LICENSE file for details.