Repository interface and implementation patterns with offline-first strategies
Generates repository interfaces and implementations with offline-first caching strategies and error handling.
/plugin marketplace add Kaakati/rails-enterprise-dev/plugin install reactree-flutter-dev@manifest-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
// lib/domain/repositories/user_repository.dart
import 'package:dartz/dartz.dart';
import '../../core/errors/failures.dart';
import '../entities/user.dart';
abstract class UserRepository {
Future<Either<Failure, User>> getUser(String id);
Future<Either<Failure, List<User>>> getAllUsers();
Future<Either<Failure, User>> createUser(User user);
Future<Either<Failure, User>> updateUser(User user);
Future<Either<Failure, void>> deleteUser(String id);
}
// lib/data/repositories/user_repository_impl.dart
import 'package:dartz/dartz.dart';
import '../../core/errors/failures.dart';
import '../../core/errors/exceptions.dart';
import '../../domain/entities/user.dart';
import '../../domain/repositories/user_repository.dart';
import '../providers/user_provider.dart';
class UserRepositoryImpl implements UserRepository {
final UserProvider _provider;
UserRepositoryImpl(this._provider);
@override
Future<Either<Failure, User>> getUser(String id) async {
try {
final model = await _provider.fetchUser(id);
return Right(model.toEntity());
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on NetworkException {
return Left(NetworkFailure());
} catch (e) {
return Left(ServerFailure('Unexpected error: $e'));
}
}
}
class UserRepositoryImpl implements UserRepository {
final UserProvider _remoteSource;
final UserLocalSource _localSource;
final NetworkInfo _networkInfo;
UserRepositoryImpl(
this._remoteSource,
this._localSource,
this._networkInfo,
);
@override
Future<Either<Failure, User>> getUser(String id) async {
if (await _networkInfo.isConnected) {
// Try remote first
try {
final model = await _remoteSource.fetchUser(id);
// Cache for offline use
await _localSource.cacheUser(model);
return Right(model.toEntity());
} on ServerException catch (e) {
// Fallback to cache on server error
return _getCachedUser(id);
}
} else {
// Use cache when offline
return _getCachedUser(id);
}
}
Future<Either<Failure, User>> _getCachedUser(String id) async {
try {
final cached = await _localSource.getCachedUser(id);
if (cached != null) {
return Right(cached.toEntity());
} else {
return Left(CacheFailure('No cached data available'));
}
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
}
}
@override
Future<Either<Failure, List<User>>> getAllUsers() async {
if (await _networkInfo.isConnected) {
try {
final models = await _remoteSource.fetchAllUsers();
await _localSource.cacheUsers(models);
return Right(models.map((m) => m.toEntity()).toList());
} on ServerException {
return _getCachedUsers();
}
} else {
return _getCachedUsers();
}
}
Future<Either<Failure, List<User>>> _getCachedUsers() async {
try {
final cached = await _localSource.getCachedUsers();
if (cached != null && cached.isNotEmpty) {
return Right(cached.map((m) => m.toEntity()).toList());
} else {
return Left(CacheFailure('No cached users'));
}
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
}
}
}
// Try cache first, then network
Future<Either<Failure, User>> getUser(String id) async {
// Check cache first
final cached = await _localSource.getCachedUser(id);
if (cached != null) {
// Return cached data immediately
_refreshInBackground(id); // Update in background
return Right(cached.toEntity());
}
// Cache miss - fetch from network
return _fetchFromNetwork(id);
}
// Try network first, fallback to cache
Future<Either<Failure, User>> getUser(String id) async {
if (await _networkInfo.isConnected) {
try {
final model = await _remoteSource.fetchUser(id);
await _localSource.cacheUser(model);
return Right(model.toEntity());
} catch (e) {
return _getCachedUser(id); // Fallback
}
} else {
return _getCachedUser(id);
}
}
// Return cache immediately, then update with network data
Stream<Either<Failure, User>> getUserStream(String id) async* {
// Emit cached data first
final cached = await _localSource.getCachedUser(id);
if (cached != null) {
yield Right(cached.toEntity());
}
// Then fetch from network
if (await _networkInfo.isConnected) {
try {
final model = await _remoteSource.fetchUser(id);
await _localSource.cacheUser(model);
yield Right(model.toEntity());
} on ServerException catch (e) {
yield Left(ServerFailure(e.message));
}
}
}
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.