smart_repository 0.1.0
smart_repository: ^0.1.0 copied to clipboard
Policy-driven coordination for remote and local repository data.
smart_repository #
Policy-driven coordination for remote and local repository data. The package has no runtime dependencies and knows nothing about HTTP clients, databases, or state-management frameworks.
Quick start #
import 'package:smart_repository/smart_repository.dart';
final repository = SmartRepository<User>(
remote: api.getUser,
local: storage.getUser,
saveLocal: storage.saveUser,
deleteLocal: storage.deleteUser,
localTimestamp: storage.getUserStoredAt,
config: const SmartRepositoryConfig(
defaultPolicy: RepositoryPolicy.networkFirst,
maxAge: Duration(minutes: 15),
),
);
final result = await repository.get();
switch (result) {
case RepositorySuccess(:final data, :final source, :final isStale):
print('$data from $source (stale: $isStale)');
case RepositoryFailure(:final error):
print(error);
}
Callbacks are normalized internally. Larger projects may instead implement
ReadDataSource<T>, WriteDataSource<T>, and DeleteDataSource and pass the
corresponding *Source arguments.
Policies #
| Policy | Contract |
|---|---|
networkOnly |
Read remote; optionally persist; fail on remote error. |
cacheOnly |
Read local only; a miss returns RepositoryCacheMissException. |
cacheFirst |
Return usable cache; stale/missing cache triggers remote; stale cache can be fallback. |
networkFirst |
Read remote; allowed remote failures fall back to stale local data. |
staleWhileRevalidate |
Return cache immediately; stale/unknown cache refreshes in background. |
cacheAndNetwork |
Return any cache immediately and always refresh in background. |
Unknown freshness is usable for cacheFirst, but refreshable for
staleWhileRevalidate. Invalidation marks cached data stale.
Streams #
watch() replays current state to every new subscriber. It emits repository
events only; it is not a state-management framework.
For stale-while-revalidate with stale cache, order is:
RepositoryLoading
RepositoryData(local)
RepositoryRefreshing(local)
RepositoryData(remote)
The operation result and stream state are separate models. To avoid a Dart
name collision, one-shot errors use RepositoryFailure; stream errors use
RepositoryFailureState.
Errors and fallback #
Errors remain generic. mapError can translate transport-specific exceptions,
then fallbackWhen decides whether local fallback is safe. This prevents cases
such as silently serving a cached user after an authorization failure.
final repository = SmartRepository<User>(
remote: api.getUser,
local: storage.getUser,
mapError: (error, stackTrace) => parseApiError(error),
fallbackWhen: (error) => error is OfflineFailure,
);
Concurrent remote reads are deduplicated by default. refresh(force: true)
bypasses an in-flight request. Call dispose() when repository lifecycle ends.
Scope #
Version 0.1 focuses on single-value and basic list reads. Keyed repository families, pagination, mutations, cancellation, and observer integrations are future additions.