Areas Needing Team Input
These are real-world challenges we’ve encountered in our MOFA architecture implementation. Each area needs team discussion to establish best practices and patterns.
🔄 Complex Cache Handler Patterns
Section titled “🔄 Complex Cache Handler Patterns”The Problem
Section titled “The Problem”Our hotel bookings cache handler has grown to over 1800 lines and manages:
- Count query caches (multiple status filters)
- Single item caches (with optimistic updates)
- List query caches (current, target status, all status)
- Cross-status cache invalidation
- Optimistic update rollback
Current Implementation Challenges
Section titled “Current Implementation Challenges”class HotelBookingsUpsertCacheHandlerStrategy { @override UpdateCacheHandler build(RequestContext requestContext) { return (proxy, response) { // 1800+ lines of cache management logic _updateCountQueryCaches(proxy, requestContext, updatedItem); _updateSingleItemCache(proxy, requestContext, updatedItem); _updateListQueryCaches(proxy, requestContext, updatedItem, isCreateOperation); _updateTargetStatusCaches(proxy, requestContext, updatedItem); _updateAllStatusListCaches(proxy, requestContext, updatedItem); // ... many more cache update methods }; }}Questions for Team Discussion
Section titled “Questions for Team Discussion”- Should we break complex cache handlers into smaller, focused handlers?
- Should we create a
CacheHandlerOrchestratorpattern? - How do we handle cache handlers that need to update 10+ different cache variations?
- Should we implement automatic cache invalidation strategies?
- What’s the maximum acceptable size for a single cache handler?
Potential Solutions to Evaluate
Section titled “Potential Solutions to Evaluate”Option 1: Cache Handler Composition
Section titled “Option 1: Cache Handler Composition”class HotelBookingsUpsertCacheHandlerStrategy { final List<CacheHandler> handlers = [ CountCacheHandler(), ListCacheHandler(), SingleItemCacheHandler(), StatusTransitionCacheHandler(), ];}Option 2: Cache Handler Orchestrator
Section titled “Option 2: Cache Handler Orchestrator”class CacheHandlerOrchestrator { void orchestrate(CacheUpdateContext context) { for (final handler in getApplicableHandlers(context)) { handler.handle(context); } }}🔄 Conversion Extension Organization
Section titled “🔄 Conversion Extension Organization”The Problem
Section titled “The Problem”We frequently forget to add new fields to conversion extensions and only discover missing fields when requests fail at runtime.
Current Pain Points
Section titled “Current Pain Points”- Runtime Discovery: Missing fields only found when executing requests
- Scattered Extensions: No clear organization pattern
- Manual Maintenance: Easy to forget updating all related extensions
- Testing Gaps: Incomplete coverage of conversion scenarios
Example of the Problem
Section titled “Example of the Problem”// Domain model gets new fieldclass HotelBookingsUpsertVars { final String? specialDietaryRequirements; // NEW FIELD // ... other fields}
// But conversion extension is not updatedextension HotelBookingsUpsertVarsExtension on HotelBookingsUpsertVars { GMutateHotelBookingsSaveVars get toMutationVars { return GMutateHotelBookingsSaveVars( (b) => b ..hotelBookingsArgs.replace( GHotelBookingsArgs( (args) => args ..id = id ..guestName = guestName // Missing: specialDietaryRequirements ), ), ); }}Questions for Team Discussion
Section titled “Questions for Team Discussion”- Can we generate conversion extensions automatically from domain models?
- How can we catch missing fields at compile time instead of runtime?
- Should we group extensions by entity, operation type, or GraphQL schema?
- What testing strategy ensures all fields are properly converted?
- Should conversion extensions be self-documenting with field mappings?
Potential Solutions to Evaluate
Section titled “Potential Solutions to Evaluate”Option 1: Code Generation
Section titled “Option 1: Code Generation”// Generate extensions from domain models@GenerateConversionExtensions()class HotelBookingsUpsertVars { // Automatically generates toMutationVars extension}Option 2: Compile-Time Validation
Section titled “Option 2: Compile-Time Validation”// Annotation-based validation@ValidateConversion(target: GMutateHotelBookingsSaveVars)extension HotelBookingsUpsertVarsExtension on HotelBookingsUpsertVars { // Compiler checks all fields are mapped}Option 3: Test-Driven Validation
Section titled “Option 3: Test-Driven Validation”void main() { test('all domain fields are converted', () { final domainFields = reflectClass(HotelBookingsUpsertVars).declarations.keys; final conversionFields = getConvertedFields(HotelBookingsUpsertVarsExtension); expect(conversionFields, containsAll(domainFields)); });}🔄 Context Parameters Documentation
Section titled “🔄 Context Parameters Documentation”The Problem
Section titled “The Problem”Context parameters are powerful but poorly documented. Team members don’t understand when and how to use them effectively.
Current Usage Patterns
Section titled “Current Usage Patterns”class CountRequestParams { final String conferenceId; // GraphQL variable final String? statusId; // GraphQL variable
// Context parameters - not part of GraphQL but needed for logic final String? userRole; // For authorization logic final String? subscriptionId; // For real-time updates final Map<String, dynamic>? metadata; // For additional context}Questions for Team Discussion
Section titled “Questions for Team Discussion”- What should be a context parameter vs a GraphQL variable?
- How do we document the purpose of each context parameter?
- Should context parameters have standardized naming conventions?
- How do context parameters flow through datasource → repository → service layers?
- What’s the best way to handle optional context parameters?
Documentation Needs
Section titled “Documentation Needs”Clear Guidelines Needed
Section titled “Clear Guidelines Needed”- When to use context parameters vs GraphQL variables
- How context parameters affect caching strategies
- Best practices for parameter naming and organization
- Examples of common context parameter patterns
🔄 Request Strategy Inheritance Patterns
Section titled “🔄 Request Strategy Inheritance Patterns”The Problem
Section titled “The Problem”We have successful patterns like the hotel booking count request strategies, but no clear guidelines for when and how to use inheritance.
Current Success Pattern
Section titled “Current Success Pattern”// Base class with shared logicabstract class HotelBookingsCountRequestStrategy { String get statusKey; // Abstract property
GQueryHotelBookingsCountReq createRequest(CountRequestParams params) { // Shared implementation with status-specific behavior }}
// Concrete implementationsclass HotelBookingsRequestedCountRequestStrategy extends HotelBookingsCountRequestStrategy { @override String get statusKey => 'requested';}Questions for Team Discussion
Section titled “Questions for Team Discussion”- When should we use inheritance vs composition for request strategies?
- What’s the maximum depth of inheritance we should allow?
- How do we balance code reuse with strategy simplicity?
- Should we create base strategy classes for common patterns?
- How do we test inherited strategies effectively?
🔄 Datasource Module Evolution
Section titled “🔄 Datasource Module Evolution”The Problem
Section titled “The Problem”The datasource module pattern works well but needs refinement as we scale to more complex features.
Current Implementation
Section titled “Current Implementation”class HotelBookingsDatasourceModule { HotelBookingsDatasource create(GqlClient client, RequestContext requestContext) { // Register 15+ strategies // Register 8+ cache handlers // Configure complex relationships }}Questions for Team Discussion
Section titled “Questions for Team Discussion”- Should modules be feature-based or operation-based?
- How do we handle cross-feature strategy dependencies?
- Should we support conditional strategy registration?
- What’s the best way to organize strategies within modules?
- How do we test module registration without integration tests?
📋 Action Items for Team
Section titled “📋 Action Items for Team”Immediate Discussions Needed
Section titled “Immediate Discussions Needed”- Cache Handler Complexity - Schedule architecture review session
- Conversion Extensions - Evaluate code generation options
- Context Parameters - Create documentation standards
- Strategy Inheritance - Establish guidelines and patterns
Research Tasks
Section titled “Research Tasks”- Code Generation Tools - Investigate build_runner solutions for extensions
- Cache Patterns - Research industry patterns for complex cache management
- Testing Strategies - Develop comprehensive testing approaches
- Documentation Tools - Evaluate tools for self-documenting code
Decision Timeline
Section titled “Decision Timeline”- Week 1: Team discussion on cache handler patterns
- Week 2: Conversion extension strategy decision
- Week 3: Context parameter documentation standards
- Week 4: Implementation plan for chosen solutions
These discussions will shape the evolution of our MOFA architecture. Each decision should be documented as an ADR (Architecture Decision Record) once we reach consensus.
How to Contribute to These Discussions
Section titled “How to Contribute to These Discussions”Weekly Architecture Reviews
Section titled “Weekly Architecture Reviews”- Bring real examples from your current work
- Share pain points you’ve encountered
- Propose solutions you’ve tried
Documentation PRs
Section titled “Documentation PRs”- Add examples of successful patterns
- Document anti-patterns you’ve discovered
- Contribute test cases for complex scenarios
Slack Discussions
Section titled “Slack Discussions”- Share quick questions and insights
- Post code snippets for review
- Discuss implementation challenges
The goal is to evolve our architecture based on real-world experience while maintaining the principles that make MOFA effective for our team.