Cache Handling Strategies
Problem
You need to implement cache handlers that manage multiple cache types (count, list, single) and handle complex update scenarios.
Team Input Needed
Complex cache handlers managing multiple caches need team input for enhancement patterns. Current handlers can exceed 1800 lines and need better organization strategies.
Simple Cache Handler
Section titled “Simple Cache Handler”Basic cache handler for single operations:
class HotelBookingsCreateCacheHandlerStrategy implements CacheHandlerStrategy< GMutateHotelBookingsSaveData, GMutateHotelBookingsSaveVars > {
@override UpdateCacheHandler build(RequestContext requestContext) { return (proxy, response) { if (response.hasErrors || response.data?.hotelbookingsSave == null) { return; }
final newItem = response.data!.hotelbookingsSave!;
_updateListCache(proxy, requestContext, newItem); _updateCountCache(proxy, requestContext); }; }
void _updateListCache( CacheProxy proxy, RequestContext requestContext, GMutateHotelBookingsSaveData_hotelbookingsSave newItem, ) { final listRequest = requestContext.getHolder<GQueryHotelBookingsReq>( HotelBookingsRequestStrategyKeys.hotelBookingsList.name, )?.request;
if (listRequest == null) return;
final existingData = proxy.readQuery(listRequest); if (existingData?.hotelbookings == null) return;
final existingItems = existingData!.hotelbookings!.hotelbookingsItems?.toList() ?? []; final convertedItem = _convertToListItem(newItem);
final updatedData = existingData.rebuild((b) => b ..hotelbookings.hotelbookingsItems.insert(0, convertedItem) ..hotelbookings.totalCount = (existingData.hotelbookings!.totalCount ?? 0) + 1 );
proxy.writeQuery(listRequest, updatedData); }
@override String get key => HotelBookingsCacheHandlerStrategyKeys.hotelBookingsCreateCacheHandler.name;}Complex Cache Handler Structure
Section titled “Complex Cache Handler Structure”For complex scenarios, organize cache handlers by responsibility:
class HotelBookingsUpsertCacheHandlerStrategy implements CacheHandlerStrategy< GMutateHotelBookingsSaveData, GMutateHotelBookingsSaveVars > {
@override UpdateCacheHandler build(RequestContext requestContext) { return (proxy, response) { if (response.hasErrors || response.data?.hotelbookingsSave == null) { print('❌ Cache update failed: ${response.graphqlErrors}'); return; }
final updatedItem = response.data!.hotelbookingsSave!; final isCreateOperation = _isCreateOperation(requestContext);
_updateCountQueryCaches(proxy, requestContext, updatedItem); _updateSingleItemCache(proxy, requestContext, updatedItem); _updateListQueryCaches(proxy, requestContext, updatedItem, isCreateOperation); }; }
void _updateCountQueryCaches( CacheProxy proxy, RequestContext requestContext, GMutateHotelBookingsSaveData_hotelbookingsSave updatedItem, ) { final statusId = updatedItem.bookingStatus?.firstOrNull?.id; if (statusId != null) { _updateSpecificStatusCountCache(proxy, requestContext, statusId); _updateAllStatusCountCache(proxy, requestContext); } }
void _updateListQueryCaches( CacheProxy proxy, RequestContext requestContext, GMutateHotelBookingsSaveData_hotelbookingsSave updatedItem, bool isCreateOperation, ) { _updateCurrentListCache(proxy, requestContext, updatedItem, isCreateOperation); _updateTargetStatusCaches(proxy, requestContext, updatedItem); _updateAllStatusListCaches(proxy, requestContext, updatedItem); }
void _updateSingleItemCache( CacheProxy proxy, RequestContext requestContext, GMutateHotelBookingsSaveData_hotelbookingsSave updatedItem, ) { final singleRequest = requestContext.getHolder<GQueryHotelBookingsReq>( HotelBookingsRequestStrategyKeys.hotelBookingsSingle.name, )?.request;
if (singleRequest == null) return;
final convertedItem = _convertToQueryItem(updatedItem); final updatedData = GQueryHotelBookingsData( (b) => b ..hotelbookings = GQueryHotelBookingsData_hotelbookings( (hb) => hb ..hotelbookingsItems.add(convertedItem) ..totalCount = 1, ).toBuilder(), );
proxy.writeQuery(singleRequest, updatedData); }}Cache Handler Context
Section titled “Cache Handler Context”Pass custom data to cache handlers using context:
class HotelBookingsUpsertRequestStrategy { @override GMutateHotelBookingsSaveReq build( UpsertRequestParams<GMutateHotelBookingsSaveVars> params, ) { _request = GMutateHotelBookingsSaveReq( (b) => b ..vars = params.vars.toBuilder() ..requestId = '${requestId}_${params.vars.hotelBookingsArgs?.id ?? 'new'}' ..updateCacheHandlerKey = HotelBookingsCacheHandlerStrategyKeys .hotelBookingsUpsertCacheHandler.name ..updateCacheHandlerContext = params.context?.toJson(), ); return _request!; }}Using Context in Cache Handler
Section titled “Using Context in Cache Handler”Access context data for conditional cache updates:
UpdateCacheHandler build(RequestContext requestContext) { return (proxy, response) { final context = response.operationRequest.updateCacheHandlerContext;
if (context != null) { final customContext = HotelBookingCacheContext.fromJson(context);
if (customContext.skipListUpdate) { return; }
if (customContext.targetStatusId != null) { _updateSpecificStatusCache(proxy, requestContext, customContext.targetStatusId!); } }
// Continue with normal cache updates... };}Cache Context Model
Section titled “Cache Context Model”Define context models for cache handler communication:
@freezed@JsonSerializable(includeIfNull: false)class HotelBookingCacheContext with _$HotelBookingCacheContext { const factory HotelBookingCacheContext({ bool? skipListUpdate, String? targetStatusId, String? conferenceId, Map<String, dynamic>? metadata, }) = _HotelBookingCacheContext;
factory HotelBookingCacheContext.fromJson(Map<String, dynamic> json) => _$HotelBookingCacheContextFromJson(json);
Map<String, dynamic> toJson() => _$HotelBookingCacheContextToJson(this);}Cache Handler Registration
Section titled “Cache Handler Registration”Register cache handlers in the datasource module:
class HotelBookingsDatasourceModule { HotelBookingsDatasource create(GqlClient client, RequestContext requestContext) { final datasource = HotelBookingsDatasource( client: client, requestContext: requestContext, );
requestContext.registerCacheHandlerStrategy( HotelBookingsUpsertCacheHandlerStrategy(), ); requestContext.registerCacheHandlerStrategy( HotelBookingsListCacheHandlerStrategy(), );
return datasource; }}Team Discussion Points
Section titled “Team Discussion Points”Complex Cache Handler Challenges:
- Size Management - Handlers can grow to 1800+ lines
- Multiple Cache Types - Count, list, single, filtered variations
- Cross-Status Updates - Status changes affect multiple caches
- Error Recovery - Rollback strategies for failed updates
Proposed Solutions:
- Handler Composition - Break into smaller, focused handlers
- Cache Orchestrator - Coordinate multiple cache updates
- Strategy Registry - Dynamic handler selection based on context
- Automatic Invalidation - Smart cache invalidation patterns
Next Steps
Section titled “Next Steps”- Understand subscription context usage
- Learn repository data cleaning
- Explore repository context patterns