Skip to content

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.

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;
}

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);
}
}

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!;
}
}

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...
};
}

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);
}

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;
}
}

Complex Cache Handler Challenges:

  1. Size Management - Handlers can grow to 1800+ lines
  2. Multiple Cache Types - Count, list, single, filtered variations
  3. Cross-Status Updates - Status changes affect multiple caches
  4. Error Recovery - Rollback strategies for failed updates

Proposed Solutions:

  1. Handler Composition - Break into smaller, focused handlers
  2. Cache Orchestrator - Coordinate multiple cache updates
  3. Strategy Registry - Dynamic handler selection based on context
  4. Automatic Invalidation - Smart cache invalidation patterns