Why MOFA Architecture?
MOFA architecture is designed to be our shared foundation—making everyone’s life easier, not adding friction.
The Problem We Solved
Section titled “The Problem We Solved”Before MOFA Architecture
Section titled “Before MOFA Architecture”Our team faced several challenges when building Flutter applications:
- Inconsistent Patterns: Each developer followed their own approach, leading to unpredictable code structure
- Repetitive Work: Constantly reinventing solutions for common problems like state management, data fetching, and caching
- Difficult Code Reviews: Similar features resulted in vastly different implementations, making PRs hard to review
- Onboarding Friction: New team members struggled to understand different patterns across projects
- Maintenance Burden: Long-term projects became increasingly difficult to maintain and extend
The Turning Point
Section titled “The Turning Point”We realized we needed a systematic approach when:
- Feature Development Slowed: Simple features took longer because developers had to make architectural decisions from scratch
- Bug Introduction Increased: Lack of testing standards led to regressions when adding new features
- Knowledge Silos Formed: Each project had its own “expert” who understood its unique patterns
- Cross-Project Movement Became Difficult: Developers couldn’t easily switch between projects
Our Solution: MOFA Architecture
Section titled “Our Solution: MOFA Architecture”Core Benefits
Section titled “Core Benefits”Consistent Development
Section titled “ Consistent Development”Problem: Each project had different folder structures, naming conventions, and architectural patterns.
Solution: Standardized four-layer architecture with clear responsibilities and boundaries.
Result: Developers can move between projects confidently, knowing exactly where to find and place code.
Reduced Repetitive Work
Section titled “ Reduced Repetitive Work”Problem: Teams constantly reimplemented GraphQL clients, state management, caching, and error handling.
Solution: Reusable patterns and generic packages that solve common problems once.
Result: Focus shifts from infrastructure to business logic and user features.
Predictable Pull Requests
Section titled “ Predictable Pull Requests”Problem: Similar features resulted in completely different implementations, making code reviews inconsistent.
Solution: Standardized patterns for services, notifiers, repositories, and UI components.
Result: Code reviews focus on business logic rather than architectural decisions.
Disciplined Workflow
Section titled “ Disciplined Workflow”Problem: Without clear boundaries, business logic leaked into UI components and data access mixed with presentation.
Solution: Strict layer boundaries with defined responsibilities and data flow.
Result: Clean separation of concerns that’s easy to test and maintain.
Faster Feedback Cycles
Section titled “ Faster Feedback Cycles”Problem: Tightly coupled code made testing difficult and deployment risky.
Solution: Modular architecture with comprehensive testing strategy focused on critical areas.
Result: Rapid iteration with confidence in code quality.
Long-term Maintainability
Section titled “ Long-term Maintainability”Problem: Projects became increasingly difficult to maintain as they grew in complexity.
Solution: Clear architectural boundaries, comprehensive documentation, and evolution-friendly patterns.
Result: Projects remain maintainable and extensible over years of development.
Key Architectural Decisions
Section titled “Key Architectural Decisions”Four-Layer Architecture
Section titled “Four-Layer Architecture”Why Four Layers?
We experimented with different approaches:
- No Architecture: Fast initially, but became unmaintainable
- Three Layers: Lacked clear separation between business logic and data access
- Five+ Layers: Over-engineered for Flutter applications, added unnecessary complexity
Four layers provide the sweet spot: Clear separation without over-engineering.
GraphQL Strategy Pattern
Section titled “GraphQL Strategy Pattern”The Problem: Ferry’s documentation examples create circular dependencies with Riverpod:
client → cache handlers → requests → clientdependency cycle- Ferry assumes global definitions, but real-world needs dynamic state
- Cannot use Riverpod providers with Ferry’s trivial examples
Our Solution: Strategy pattern with DatasourceModule that:
- Eliminates circular dependencies
- Enables context-aware cache operations
- Supports dynamic state in GraphQL operations
- Maintains type safety and code generation benefits
Caching Service Instead of Local Storage Datasource
Section titled “Caching Service Instead of Local Storage Datasource”The Problem: Traditional approaches mix data access concerns:
- Local storage logic scattered across repositories
- Difficult to implement different caching strategies
- Testing becomes complex with mixed responsibilities
Our Solution: Dedicated caching service with three types:
- Secure Cache: For sensitive data (tokens, credentials)
- Simple Cache: For basic data (preferences, settings)
- Complex Cache: For advanced scenarios (offline sync, TTL)
Core Folder vs Core Package
Section titled “Core Folder vs Core Package”The Problem: Generic “core” packages become dumping grounds:
- Mixed concerns in a single package
- Tight coupling between unrelated utilities
- Difficult to test and maintain
Our Solution: App-specific core folder containing only:
- Features that other features depend on (Auth)
- Shared infrastructure (Router, Service Locators)
- Configuration (Flavors, App Settings)
Real-World Impact
Section titled “Real-World Impact”Qualitative Improvements
Section titled “Qualitative Improvements”- Increased Confidence: Developers feel confident making changes across projects
- Better Collaboration: Shared vocabulary and patterns improve team communication
- Reduced Stress: Clear guidelines eliminate decision paralysis
- Higher Quality: Consistent patterns lead to more robust applications
Evolution and Adaptation
Section titled “Evolution and Adaptation”Living Architecture
Section titled “Living Architecture”MOFA architecture isn’t static—it evolves based on:
- Team Experience: Lessons learned from real projects
- Technology Changes: Updates to Flutter, Dart, and ecosystem packages
- Project Requirements: New challenges that require architectural adaptations
- Community Feedback: Insights from other teams and open source contributions
Continuous Improvement
Section titled “Continuous Improvement”We regularly review and update our architecture through:
- Weekly Architecture Reviews: Discuss patterns and improvements
- Monthly Deep Dives: Analyze specific architectural decisions
- Quarterly Planning: Major architectural changes and technology evaluation
- Project Retrospectives: Learn from successes and challenges
When MOFA Architecture Fits
Section titled “When MOFA Architecture Fits”Ideal Use Cases
Section titled “Ideal Use Cases”MOFA architecture works best for:
- Team Development: Multiple developers working on the same codebase
- Complex Applications: Apps with significant business logic and data management
- Long-term Projects: Applications that will be maintained and extended over time
- Multiple Projects: Teams building several related applications
When to Consider Alternatives
Section titled “When to Consider Alternatives”MOFA architecture might be overkill for:
- Prototypes: Quick experiments or proof-of-concepts
- Short-term Projects: Applications with limited lifespan
Getting Started
Section titled “Getting Started”If MOFA architecture sounds right for your team:
- Start with Tutorials: Learn the fundamentals through hands-on examples
- Read the Explanations: Understand the reasoning behind each decision
- Use How-to Guides: Implement specific patterns in your projects
- Reference Documentation: Look up details as you build
Remember: Architecture should serve your team, not the other way around. Adapt these patterns to fit your specific needs and context.
This architecture evolves with our team’s experience. Found something that doesn’t work? Have a better approach? Let’s discuss it! Every improvement makes our shared foundation stronger.