# uta-ecosystem: UTA Ecosystem Overview
URL: https://dev.updatetheapp.com/docs/uta-ecosystem
Documentation: https://dev.updatetheapp.com/docs/uta-ecosystem
Understanding how the UTA ecosystem components work together to accelerate React Native development while maintaining platform independence.
***
title: UTA Ecosystem Overview
description: Understanding how the UTA ecosystem components work together to accelerate React Native development while maintaining platform independence.
---------------------------------------------------------------------------------------------------------------------------------------------------------
import { Callout } from 'fumadocs-ui/components/callout';
import { Steps, Step } from 'fumadocs-ui/components/steps';
import { Card, Cards } from 'fumadocs-ui/components/card';
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
# UTA Ecosystem Overview
## Overview
The UTA ecosystem provides standardized React Native development patterns and deployment solutions that work independently or together. Rather than creating vendor lock-in, UTA offers proven architectural patterns that enhance any React Native project, regardless of your chosen deployment strategy.
This approach eliminates architectural decision fatigue while maintaining complete freedom in your deployment choices. Whether you use Expo Updates, CodePush, updatetheapp.com, or any other solution, UTA patterns provide the foundation for scalable, maintainable applications.
**What Makes UTA Different**
UTA provides architectural guidance that works with **any** deployment solution. The patterns stand on their own merit and can be adopted independently of any specific platform or service.
## Purpose & Scope
This document explains how UTA's components work together while maintaining independence, helping you understand how to adopt UTA patterns regardless of your deployment choices.
**Target Audience**: Architects, technical leads, and development teams evaluating standardized development approaches for React Native projects.
## Prerequisites
To get the most out of this architectural overview, we recommend you first understand these key areas and review the related guides:
* Basic familiarity with React Native development patterns and common challenges
* Understanding of modern JavaScript/TypeScript development workflows and tooling
* Awareness of over-the-air (OTA) update concepts and deployment options in the React Native ecosystem
For implementation-specific guidance and step-by-step instructions, see the [UI Development Getting Started](/docs/ui-development/getting-started) guide after understanding the ecosystem foundations.
## Core Ecosystem Components
### Independent Yet Complementary Components
UTA consists of three independent components that can work together or separately:
Works with any deployment solution
One deployment option among many
Educational resource for any React Native project
**Key Principle**: Each component provides independent value while working well together when combined.
### Platform-Agnostic Development Foundation
**Purpose**: A production-ready React Native template with battle-tested architectural patterns
**Core Principles**:
* **Deployment Neutral**: Works with Expo Updates, CodePush, updatetheapp.com, or any OTA solution
* **Proven Patterns**: Pre-selected architectural approaches that have been tested across different projects
* **Immediate Productivity**: Fork, setup, and start building features from day one
**What's Included**:
* Feature-Domain Hybrid architecture (UI organization + business logic separation)
* Standardized state management patterns with Zustand
* API integration patterns using TanStack Query
* Comprehensive testing strategies and utilities
* AI-optimized code patterns for better development assistance
**Benefits for Your Team**:
* Eliminates architectural decision paralysis across projects
* Ensures consistent code patterns regardless of deployment choice
* Dramatically reduces onboarding time for new developers
* Provides immediate AI assistance with high accuracy
### Choose Your Deployment Strategy
**Purpose**: UTA patterns work with any React Native deployment approach
**Supported Deployment Options**:
* **Expo Updates**: Native integration with Expo's OTA update system
* **CodePush**: Microsoft's OTA update solution for React Native
* **updatetheapp.com**: UTA's own deployment platform with advanced features
* **Custom Solutions**: Any deployment strategy that supports JavaScript bundle updates
**Platform-Specific Optimizations**:
* Configuration templates for popular deployment services
* Environment-specific build scripts and configurations
* Deployment workflow examples for different platforms
* Migration guides between deployment solutions
**No Vendor Lock-in**:
* Template code remains independent of deployment choice
* Switch deployment platforms without architectural changes
* Test different deployment strategies without code modifications
### Educational Resource Architecture
**Purpose**: Comprehensive documentation that serves development needs regardless of deployment choice
**Documentation Strategy**:
* **Development Patterns**: Architectural guidance that applies to any React Native project
* **Deployment Examples**: Show multiple deployment options, not just one platform
* **AI Training Context**: Machine-readable patterns optimized for AI assistance
**Key Benefits**:
* Learn patterns that transfer to any React Native project
* Compare different deployment strategies with real examples
* Access consistent architectural guidance regardless of your platform choices
* Get AI assistance that understands modern React Native patterns
**Educational Philosophy**:
* Explain the "why" behind architectural decisions
* Show multiple implementation approaches when relevant
* Provide migration paths between different solutions
* Focus on long-term maintainability over vendor-specific features
## How the Components Work Together
The UTA ecosystem creates value through **independent excellence** rather than forced integration:
**Template** → **Documentation** → **Deployment Choice**
* **react-native-uta template** provides proven architectural patterns that work anywhere
* **UTA DevHub documentation** offers comprehensive guidance for any React Native project
* **Your deployment choice** can be any solution that fits your needs (Expo, CodePush, updatetheapp.com, custom)
This independent approach ensures that adopting UTA patterns doesn't lock you into any specific deployment platform, while providing examples of how these patterns work well with various deployment strategies.
## Documentation Strategy: Platform-Neutral Guidance
UTA documentation focuses on architectural patterns that work with any deployment solution, while providing specific examples for popular platforms.
### Development Focus: UTA DevHub (This Site)
**Purpose**: UTA DevHub - comprehensive development guide for platform-agnostic React Native patterns\
**Target Audience**: Developers building applications with modern React Native patterns
**What You'll Find Here**:
* Architecture patterns and design decisions with detailed explanations
* UI component development guidelines and best practices
* State management implementation using modern patterns
* API integration approaches with TanStack Query
* Development workflows that work with any deployment platform
### Platform-Specific Examples
**Purpose**: Show how UTA patterns work with different deployment solutions\
**Target Audience**: Teams evaluating or implementing specific deployment strategies
**Deployment Examples Covered**:
* Expo Updates configuration and workflow
* CodePush integration and release management
* updatetheapp.com deployment and monitoring
* Custom deployment pipeline setup
**Educational Approach**
* **How to architect** → UTA DevHub patterns (platform-neutral)
* **How to deploy** → Multiple examples showing different platforms
* **How to choose** → Decision frameworks for evaluating deployment options
## Complete Developer Journey
Understanding how UTA works in practice, regardless of your deployment choice:
### Foundation: Start with Proven Architecture
Begin with the react-native-uta template that provides battle-tested patterns:
**What Happens**:
* Fork a production-ready template with established architectural patterns
* Run automated setup that configures your development environment
* Access comprehensive documentation and AI-optimized patterns
**Immediate Benefits**:
* Day one productivity with zero architectural decisions required
* Consistent patterns that work with any deployment strategy
* AI assistance that understands your codebase from the start
**Why This Matters**: Instead of spending weeks debating state management libraries or folder structures, your team starts building features immediately with patterns that work everywhere.
### Development: Build with Proven Patterns
Develop features using pre-established, deployment-agnostic architectural patterns:
**Standardized Patterns**:
* **State Management**: Zustand for predictable, scalable state handling
* **API Layer**: TanStack Query for robust data fetching and caching
* **UI Architecture**: Feature-Domain Hybrid system for maintainable organization
* **Testing Strategy**: Comprehensive testing patterns built into the template
**Key Principle**: Follow proven patterns that enhance maintainability regardless of your deployment platform.
**Team Benefits**: Code reviews become faster, onboarding becomes easier, and everyone writes code that works consistently across different deployment environments.
### Deployment: Choose Your Strategy
Deploy using whichever solution best fits your needs:
**Popular Options**:
```bash
# Expo Updates
expo publish --channel production
# CodePush
appcenter codepush release-react MyApp-iOS
# updatetheapp.com
uta deploy --production
# Custom solution
npm run build:release && ./deploy-script.sh
```
**Business Impact**: Ship updates using the deployment strategy that best matches your team's needs and constraints.
**Real-World Flexibility**: Teams have successfully used UTA patterns with Expo Updates for rapid prototyping, CodePush for enterprise deployment, updatetheapp.com for managed deployment, and custom solutions for specialized requirements.
### Iteration: Maintain Development Velocity
Maintain momentum with your chosen deployment approach:
**Continuous Improvement**:
* Monitor application performance and user behavior
* Push feature updates using your preferred deployment method
* Switch deployment platforms if needs change (architectural patterns remain the same)
* All operations work independently of UTA-specific services
**Long-term Benefits**: Your team maintains high velocity because architectural patterns remain consistent even if deployment strategies evolve.
## Why This Ecosystem Approach Works
### Individual Developer Benefits
**Platform-Independent Productivity**:
* **(Do ✅) Day 1 Productivity**: Fork and start building immediately with any deployment target
* **(Do ✅) Eliminate Decision Fatigue**: Proven patterns work consistently across platforms
* **(Do ✅) AI-Assisted Development**: Achieve high accuracy with standardized patterns
* **(Do ✅) Deployment Flexibility**: Ship updates using the platform that best fits your project
**Learning & Growth**:
* Learn industry-standard patterns that transfer to any React Native project
* Focus on business logic instead of architectural plumbing
* Get immediate feedback through consistent, predictable development patterns
* Build confidence with patterns that work regardless of deployment choice
### Team Collaboration Benefits
**Organizational Efficiency**:
* **(Do ✅) Reduced Context Switching**: Standardized patterns mean less time debating approaches
* **(Do ✅) Faster Onboarding**: New developers become productive within days, not weeks
* **(Do ✅) Streamlined Code Reviews**: Everyone follows consistent patterns and conventions
* **(Do ✅) Deployment Flexibility**: Team can evaluate and switch deployment platforms without architectural changes
**Collaboration Improvements**:
* Shared vocabulary and patterns reduce miscommunication
* Standardized architecture makes it easy to work on any part of the codebase
* Consistent testing patterns ensure reliable quality across all deployment targets
* Documentation that everyone can contribute to and benefit from
### Strategic Business Advantages
**Competitive Benefits**:
* **(Do ✅) Ship Faster**: From code to production using your preferred deployment method
* **(Do ✅) Risk Reduction**: Proven patterns reduce architectural risks across platforms
* **(Do ✅) Platform Independence**: Avoid vendor lock-in with deployment-agnostic patterns
* **(Do ✅) Strategic Flexibility**: Evaluate and adopt different deployment strategies as needs evolve
**Operational Efficiency**:
* Reduced development costs through faster onboarding and fewer architectural mistakes
* Lower maintenance overhead with standardized, well-documented patterns
* Improved reliability through battle-tested architectural decisions
* Better resource allocation since teams spend time on features, not infrastructure decisions
**Important Considerations**:
* **(Be Aware ❗) Learning Investment**: Teams need time to adapt to standardized patterns initially
* **(Be Aware ❗) Platform Evaluation**: Teams should evaluate deployment options based on their specific needs
* **(Consider 🤔) Deployment Strategy**: Choose deployment platforms based on requirements, not architectural constraints
## Implementation Considerations
### Performance & Scalability
**Deployment-Agnostic Optimization**:
* Monitor bundle size to ensure fast updates regardless of deployment platform
* Implement code splitting patterns that work with any OTA update system
* Balance update frequency with user experience across different deployment strategies
**Application Performance**:
* Built-in performance monitoring through standardized template patterns
* Established patterns for code splitting and lazy loading
* Proven state management patterns that scale with application complexity
### Security & Reliability
**Platform-Independent Security**:
* Secure coding patterns that work with any deployment strategy
* Authentication and authorization patterns that don't depend on specific platforms
* Data protection strategies that remain consistent across deployment environments
**Operational Security**:
* Template includes security best practices for any deployment platform
* Audit trail capabilities that work with multiple deployment solutions
* Monitoring and alerting patterns that adapt to different platforms
### Scalability Aspects
**Team Scaling**:
* Standardized patterns support teams from 1 to 100+ developers
* Consistent architecture reduces coordination overhead as teams grow
* Documentation and AI assistance provide self-service onboarding
**Technical Scaling**:
* Architecture patterns proven to work at scale with real-world applications
* Built-in performance patterns and optimization strategies
* Deployment infrastructure patterns designed for enterprise-scale applications
## Getting Started
Understand UTA's architectural principles and design decisions
Get hands-on with the react-native-uta template
## Related Documents
For deeper technical implementation details, explore these related resources:
* [UI Development Getting Started](/docs/ui-development/getting-started) - Hands-on guide to building with UTA patterns
* [State Management Architecture](/docs/state-management/state-management-architecture) - Comprehensive guide to modern state management patterns
* [API Client Architecture](/docs/api/client-architecture) - TanStack Query implementation patterns and best practices
* [Team Onboarding Checklist](/docs/guides/team-onboarding-checklist) - Step-by-step guide for new team members
**The UTA Philosophy**
UTA represents proven React Native architectural patterns that enhance any project, regardless of deployment choice. By standardizing on battle-tested approaches, your team can focus on what matters most: building exceptional features for your users.
# UTA Architecture: Architecture Documentation
URL: https://dev.updatetheapp.com/docs/architecture
Documentation: https://dev.updatetheapp.com/docs/architecture
Core architectural patterns and design philosophy of the UTA mobile application template
***
title: Architecture Documentation
description: Core architectural patterns and design philosophy of the UTA mobile application template
-----------------------------------------------------------------------------------------------------
import { Card, Cards } from 'fumadocs-ui/components/card';
import { ChevronRightIcon } from 'lucide-react';
# Architecture Documentation
## Overview
The UTA mobile application architecture follows a modular, domain-driven design that emphasizes separation of concerns, testability, and maintainability. Our architecture is designed to scale with your application's complexity while providing clear boundaries between different parts of your system.
Our architecture is organized into several interconnected layers:
* **Core Framework** provides fundamental infrastructure and utilities
* **Domain Modules** encapsulate business logic and domain-specific functionality
* **Feature Modules** implement complete user-facing features
* **UI Components** create the visual interface and user interactions
These layers interact with state management systems and external integrations to form a cohesive application architecture.
## Key Architecture Documents
} title="Design Philosophy & Tradeoffs" description="Comprehensive evaluation of architectural strengths, limitations, and decision-making framework for adoption." href="/docs/architecture/design-philosophy-and-tradeoffs" />
} title="Core Architecture" description="Understand the foundational principles, patterns, and structure of the UTA mobile application template." href="/docs/architecture/core-architecture" />
} title="Project Structure" description="Learn about the file and folder organization, and how different parts of the codebase relate to each other." href="/docs/architecture/project-structure" />
} title="Domain Architecture" description="Explore our domain-driven design approach that creates clear boundaries between different parts of your application." href="/docs/architecture/domain-architecture" />
## Architecture Philosophy
Our architecture is built around these core principles:
1. **Separation of Concerns** - Each module has a single responsibility and clear boundaries
2. **Domain-Driven Design** - Business logic is organized into cohesive domains
3. **Unidirectional Data Flow** - Predictable state management with clear update patterns
4. **Feature-First Organization** - Code organized by feature rather than technical layer
5. **Testability** - Architecture designed to facilitate comprehensive testing strategies
## Related Areas
The architecture works together with these other important aspects of the application:
} title="API Architecture" description="Learn how our API clients integrate with backend services, including authentication and error handling." href="/docs/api" />
} title="State Management" description="Understand our approach to managing application state, both globally and locally." href="/docs/state-management" />
} title="Feature Implementation" description="Discover patterns for implementing features in a consistent way across the application." href="/docs/features" />
# UTA Architecture: Design Philosophy & Tradeoffs
URL: https://dev.updatetheapp.com/docs/architecture/design-philosophy-and-tradeoffs
Documentation: https://dev.updatetheapp.com/docs/architecture/design-philosophy-and-tradeoffs
Honest evaluation of UTA's architectural patterns, design decisions, and guidance for teams considering adoption of our Feature-Domain Hybrid approach.
***
title: Design Philosophy & Tradeoffs
description: Honest evaluation of UTA's architectural patterns, design decisions, and guidance for teams considering adoption of our Feature-Domain Hybrid approach.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
import { Card, Cards } from 'fumadocs-ui/components/card';
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
# Design Philosophy & Tradeoffs
## Overview
This document provides an honest evaluation of our UTA design philosophy and the specific architectural patterns we've chosen. We believe in transparency about both the benefits and costs of our approach, helping you understand when our Feature-Domain Hybrid strategy aligns with your project needs.
Rather than claiming our architecture fits every scenario, we examine real-world tradeoffs, learning investments, and situations where simpler approaches might be more appropriate. This balanced perspective helps teams make informed decisions about whether our architectural patterns match their specific context and goals.
## Purpose & Scope
**Target Audience**: Technical decision-makers, senior developers, and team leads evaluating architectural approaches for React Native projects, particularly those considering domain-driven design patterns.
**What This Document Addresses**:
* Our core architectural decisions and the problems they solve
* Honest assessment of learning investment and complexity costs
* Decision framework for evaluating if our patterns fit your project
* Team readiness considerations and adoption strategies
* Alternative architectural approaches for different project types
**What This Document Does NOT Cover**:
* Step-by-step implementation details (see our [Implementation Guides](/docs/guides))
* Specific API documentation (see [API Reference](/docs/api))
* Development workflow specifics (see [Getting Started Guide](/docs/guides/getting-started) and [Team Onboarding](/docs/guides/team-onboarding-checklist))
## Prerequisites
To get the most value from this document, we recommend first exploring these foundational resources:
* **[Core Architecture Reference](/docs/architecture/core-architecture)** - This will help you understand our technology stack and foundational patterns
* **[Domain Architecture Guide](/docs/architecture/domain-architecture)** - Essential for understanding our domain-driven design approach
* **[Project Structure](/docs/architecture/project-structure)** - Useful for seeing how our file organization philosophy works in practice
**Technical Background**: You'll get more from the architectural discussions if you're familiar with React Native development, TypeScript, and modern state management concepts (like the difference between server state and client state).
**Technical Foundation**: Our architectural patterns work with any modern React Native setup - whether you're using Expo Router + CNG, React Native CLI, or custom configurations. The patterns focus on code organization and business logic, not the underlying technical foundation.
## UTA Architectural Design Philosophy
### Core Organizational Strategy: Feature-Domain Hybrid
After working with various React Native project structures, we've developed a **Feature-Domain Hybrid** approach that addresses common pain points we've encountered. This strategy combines the intuitive nature of feature-first organization with the business logic clarity of domain-driven design.
**Why We Chose This Approach**: Traditional feature-only organization works great initially, but as business logic becomes complex, you end up with logic scattered across UI components. Pure domain-driven approaches can feel abstract for mobile apps. Our hybrid approach gives you the best of both worlds:
Domains
Domains --> Infrastructure
style Features fill:#e3f2fd
style Domains fill:#f3e5f5
style Infrastructure fill:#e8f5e8
`}
/>
**Understanding the Layers**:
* **Features** handle everything users see and interact with (screens, navigation, UI state)
* **Domains** contain pure business logic that could work in any UI context
* **Infrastructure** provides shared services that both layers can use
This separation means you can change how features look and behave without touching business rules, and you can modify business logic without breaking UI components.
### Key Architectural Decisions & Their Benefits
We've made several opinionated decisions based on real-world React Native development challenges we've encountered. Each decision addresses specific pain points that teams commonly face as projects grow:
#### Feature-Domain Hybrid Organization
* **Features for UI**: Screen components, navigation, and user-facing logic grouped by user workflow
* **Domains for Business Logic**: Pure business rules and data models isolated from UI concerns
* **Predictable Structure**: Teams know exactly where to find and place different types of code
* **Scaling Benefits**: New features don't require restructuring existing code organization
#### Separated State Management Strategy
* **Server State**: TanStack Query handles API data, caching, and synchronization
* **Client State**: Zustand manages application-specific UI state and user preferences
* **No State Confusion**: Clear boundaries eliminate debates about where state belongs
* **Performance Optimized**: Right tool for each type of state management need
#### TypeScript-First Design
* **Domain Types**: Business entities defined as TypeScript interfaces/types
* **API Contracts**: Full type safety from backend responses to UI components
* **Component Props**: Comprehensive typing for all React Native components
* **Developer Experience**: IDE support, refactoring safety, and runtime error prevention
#### Testability by Design
* **Pure Business Logic**: Domain functions are easily unit tested without UI dependencies
* **Dependency Injection**: Services can be mocked and replaced for testing
* **Component Isolation**: UI components tested independently from business logic
* **Integration Testing**: Clear boundaries make integration testing more focused and reliable
### Problems This Architecture Solves
These patterns emerged from real challenges we've faced in React Native projects. Understanding these pain points helps explain why we've made specific architectural choices:
**Why This Matters**: These aren't theoretical problems - they're based on real experiences from teams scaling React Native applications. Each pattern we've chosen addresses specific pain points that become critical as projects grow beyond initial prototypes.
### Architectural Principles
Building on the CNG foundation, the UTA architecture is guided by these principles:
Scalable
DDD --> Maintainable
UDF --> Predictable
FTO --> Collaborative
Test --> Quality
style SoC fill:#e1f5fe
style DDD fill:#f3e5f5
style UDF fill:#e8f5e8
style FTO fill:#fff3e0
style Test fill:#fce4ec
`}
/>
#### 1. Separation of Concerns
* **Business Logic**: Isolated in domain modules, independent of UI
* **Presentation Logic**: Contained within feature modules and components
* **Infrastructure**: Shared utilities and configuration separated from business rules
#### 2. Domain-Driven Design
* **Business-Centric Organization**: Code structure reflects business domains
* **Bounded Contexts**: Clear boundaries prevent domain logic bleeding
* **Ubiquitous Language**: Consistent terminology across code and documentation
#### 3. Unidirectional Data Flow
* **Predictable State Changes**: Clear patterns for state mutations
* **Server State Management**: TanStack Query handles API interactions
* **Client State Isolation**: Zustand manages application-specific state
#### 4. Feature-First Organization
* **User-Centric Structure**: Features organized around user workflows
* **Colocated Code**: Related components, screens, and navigation grouped together
* **Encapsulation**: Features contain their own state and navigation logic
#### 5. Testability by Design
* **Dependency Injection**: Services can be easily mocked for testing
* **Pure Functions**: Business logic written as testable pure functions
* **Component Isolation**: UI components testable independently of business logic
### When This Architecture Excels
## Honest Assessment of Architectural Tradeoffs
### When Our Patterns Create Overhead
**Honest Reality**: Our architecture is optimized for projects that will grow in complexity over time. If you're building a simple app that will stay simple, this approach may feel like overkill.
#### Learning Investment You Should Expect
We believe in being upfront about the learning curve. Here's what your team should be prepared for:
* **Domain-Driven Design Concepts**: Understanding business logic separation is a mindset shift that takes time to internalize
* **State Management Patterns**: Learning when to use TanStack Query vs. Zustand requires understanding the difference between server and client state
* **TypeScript Discipline**: Our approach relies heavily on TypeScript for safety, so team members need to be comfortable with comprehensive typing
* **Architectural Thinking**: Success requires embracing layered architecture concepts rather than putting everything in components
#### Complexity Considerations
|May be overkill| Architecture[UTA Architecture]
Complex --> |Well-suited| Architecture
Architecture --> Overhead[Setup Overhead]
Architecture --> Benefits[Long-term Benefits]
style Simple fill:#ffebee
style Complex fill:#e8f5e8
style Architecture fill:#e3f2fd
style Overhead fill:#fff3e0
style Benefits fill:#f1f8e9
`}
/>
### When NOT to Use This Architecture
**Key Insight**: No architecture fits every scenario. Here's when alternatives might be better choices.
#### Project Types Where Simpler Approaches Excel
#### Rapid Prototyping & MVPs
* **Timeline**: Projects with 2-4 week development cycles
* **Scope**: Single-feature or proof-of-concept applications
* **Alternative**: Expo Router with simple folder structure
* **Why**: Setup overhead exceeds project duration
#### Small-Scale Applications
* **Team Size**: 1-2 developers
* **Feature Count**: Fewer than 5 major features
* **Complexity**: Simple CRUD operations
* **Alternative**: Standard React Native CLI with basic organization
* **Why**: Architectural benefits don't justify complexity
#### Educational Projects
* **Purpose**: Learning React Native fundamentals
* **Focus**: Understanding core concepts rather than architecture
* **Timeline**: Short-term learning exercises
* **Alternative**: Tutorial-based structures
* **Why**: Architecture concepts can distract from core learning
#### Emergency Projects
* **Constraints**: Extremely tight deadlines
* **Resources**: No time for team training
* **Scope**: Single-purpose applications
* **Alternative**: Framework defaults with minimal customization
* **Why**: Training time conflicts with delivery requirements
#### Technical Constraints
* **Legacy Integration**: Heavy integration with legacy systems that conflict with modern patterns
* **Extreme Performance**: Real-time applications with microsecond requirements (rare in mobile apps)
### Known Tradeoffs & Their Impact
#### Development Velocity Tradeoffs
Slow
Week3 --> Breaking
Week5 --> Fast
style Week1 fill:#ffebee
style Week3 fill:#fff3e0
style Week5 fill:#e8f5e8
style Slow fill:#ffcdd2
style Breaking fill:#fff9c4
style Fast fill:#c8e6c9
`}
/>
#### Performance Considerations
* **Bundle Size**: Additional dependencies increase application size
* **Memory Usage**: Multiple state management solutions consume more memory
* **Initialization Time**: Complex setup can slow application startup
* **Runtime Overhead**: Abstraction layers introduce minimal performance cost
#### Team Requirements
* **Skill Level**: Requires developers comfortable with TypeScript and modern React patterns
* **Onboarding Time**: New team members need 1-2 weeks to become productive
* **Maintenance Expertise**: Long-term success requires understanding architectural principles
* **Decision Making**: Teams must commit to following established patterns consistently
## Decision Framework
### Evaluation Criteria
We've created this framework to help you honestly assess whether our architectural approach makes sense for your specific situation. Rather than advocating for our approach in every case, we want to help you make the right choice for your project:
#### Project Characteristics
**Strong Fit Indicators** ✅
* Development timeline exceeding 3 months
* Expected feature count above 10 major features
* Multiple user roles and workflows
* Plans for ongoing feature development
* Integration with multiple backend services
**Weak Fit Indicators** ❌
* One-time prototype or proof of concept
* Simple CRUD application
* Single-purpose utility app
* Fixed scope with no planned expansion
* Minimal business logic complexity
#### Team Capabilities
**Strong Fit Indicators** ✅
* 3+ developers on the team
* Experience with TypeScript and modern React
* Commitment to learning new patterns
* Plans for team growth
* Emphasis on code quality and maintainability
**Weak Fit Indicators** ❌
* Solo developer or pair programming only
* Beginner-level React Native experience
* Tight deadlines preventing learning time
* High team turnover expected
* Preference for minimal abstractions
#### Technical Needs
**Strong Fit Indicators** ✅
* Complex state management requirements
* Multiple API integrations
* Need for comprehensive testing
* Performance optimization important
* Platform-specific customization needed
**Weak Fit Indicators** ❌
* Simple local data storage
* Single API endpoint integration
* Minimal testing requirements
* Basic performance needs
* Standard platform behaviors sufficient
#### Business Context
**Strong Fit Indicators** ✅
* Long-term product vision (1+ years)
* Multiple stakeholders and requirements
* Plans for feature iteration and evolution
* Importance of maintainability
* Budget for proper development practices
**Weak Fit Indicators** ❌
* Short-term tactical solution
* Single stakeholder with simple needs
* Fixed requirements unlikely to change
* Time-to-market prioritized over quality
* Limited development budget
### Decision Tree
TeamSize{Team Size > 2?}
TeamSize -->|No| Simple[Consider Simpler Architecture]
TeamSize -->|Yes| Timeline{Timeline > 3 months?}
Timeline -->|No| Simple
Timeline -->|Yes| Features{Features > 10?}
Features -->|No| Evaluate[Evaluate Other Factors]
Features -->|Yes| Complex{Complex Business Logic?}
Complex -->|No| Evaluate
Complex -->|Yes| UTA[UTA Architecture Recommended]
Evaluate --> Experience{Team TypeScript Experience?}
Experience -->|Low| Training{Training Time Available?}
Experience -->|High| Maintenance{Long-term Maintenance?}
Training -->|No| Simple
Training -->|Yes| UTA
Maintenance -->|No| Simple
Maintenance -->|Yes| UTA
style UTA fill:#c8e6c9
style Simple fill:#ffcdd2
style Evaluate fill:#fff3e0
`}
/>
### Questions to Discuss with Your Team
**Team Alignment**: These questions can help facilitate productive discussions about whether our approach fits your team's needs and constraints.
#### Technical Assessment
1. How comfortable is our team with TypeScript and modern React patterns?
2. Are we planning to integrate with multiple backend services or APIs?
3. Do we expect significant growth in features and complexity over time?
4. How important is comprehensive testing for this project?
#### Project Context
1. What are our realistic development timeline and team size constraints?
2. How do we balance long-term maintainability with time-to-market pressures?
3. Do we have stakeholder support for investing in architectural patterns?
4. Are we prepared to invest time in team onboarding and learning?
#### Risk Assessment
1. What are the real consequences if our architectural choice doesn't work out?
2. How difficult would it be to migrate to a different approach later?
3. Do we have the expertise to adapt these patterns to our specific needs?
4. How will we maintain architectural consistency as the team grows?
## Migration Considerations
### Adoption Strategies
#### New Project Implementation
**Recommended Approach**: Full adoption from project start
**Timeline**: 1-2 weeks for initial setup and team training
**Steps**:
1. Clone template and configure for your project
2. Conduct team training sessions on architectural patterns
3. Implement first feature following established patterns
4. Establish code review processes for pattern compliance
5. Create project-specific documentation additions
**Success Factors**:
* Dedicated time for learning and setup
* Team commitment to following patterns
* Clear communication about architectural decisions
#### Migrating Existing Codebases
**Recommended Approach**: Gradual migration by feature
**Timeline**: 2-6 months depending on codebase size
**Steps**:
1. Assess current architecture and identify pain points
2. Create migration plan prioritizing high-value features
3. Implement new features using UTA patterns
4. Gradually refactor existing features during maintenance
5. Update team processes and documentation
**Challenges**:
* Maintaining consistency during transition
* Managing technical debt during migration
* Team adoption of new patterns while maintaining old code
#### Selective Pattern Adoption
**Recommended Approach**: Adopt specific architectural patterns without full migration
**Use Cases**:
* Teams wanting domain organization without full template
* Projects requiring only state management improvements
* Gradual architectural evolution
**Considerations**:
* May miss benefits of integrated architectural approach
* Requires deeper understanding of individual patterns
* Risk of inconsistent implementation
### Team Preparation Requirements
#### Technical Skills Development
* **TypeScript Proficiency**: Essential for taking advantage of type safety
* **Modern React Patterns**: Hooks, context, and state management
* **TanStack Query**: Server state management and caching strategies
* **Testing Practices**: Component testing and integration testing approaches
#### Process Adaptations
* **Code Review Guidelines**: Establishing architectural pattern compliance
* **Documentation Habits**: Maintaining up-to-date architectural documentation
* **Feature Planning**: Incorporating domain and feature analysis into planning
* **Onboarding Processes**: Training new team members on architectural patterns
### Success Metrics
#### Short-term Indicators (First 3 months)
* Team comfort level with architectural patterns
* Consistency of pattern implementation across features
* Reduction in time spent on code organization decisions
* Quality of code reviews and architectural discussions
#### Long-term Indicators (6+ months)
* Developer velocity for new feature implementation
* Reduced time for new team member onboarding
* Decreased bug rates related to state management
* Improved test coverage and reliability
## Alternative Architectural Approaches
**UTA's Role**: UTA provides architectural patterns that can be applied on top of any React Native technical foundation (Expo Router + CNG, React Native CLI, or custom setups).
### When to Consider Different Architectural Strategies
Minimal
Rapid --> Minimal
Custom --> CustomArch
Enterprise --> UTA
style Simple fill:#e8f5e8
style Enterprise fill:#e3f2fd
style UTA fill:#c8e6c9
`}
/>
#### Simple Project Organization (Framework Defaults)
* **Best For**: Prototypes, MVPs, small teams (1-3 developers)
* **Benefits**: Minimal setup, fast initial development, easy to understand
* **Tradeoffs**: Limited scalability, no architectural guidance for complexity
#### Custom Domain-Driven Architecture
* **Best For**: Enterprise teams with specific architectural requirements, existing DDD expertise
* **Benefits**: Complete control over patterns, tailored to business context
* **Tradeoffs**: Requires significant architectural expertise, longer standardization time
#### UTA's Position as Architectural Layer
**Key Understanding**: UTA architectural patterns sit **above** the technical foundation. Whether you use Expo Router + CNG, React Native CLI, or custom configurations, UTA provides the business logic organization and state management strategies.
**When UTA Architectural Patterns Are Most Valuable**:
* Teams building complex applications that will scale beyond simple CRUD operations
* Projects requiring clear separation between UI logic and business logic
* Organizations prioritizing long-term maintainability and team collaboration
* Applications with sophisticated state management needs (multiple APIs, complex caching)
**When Simpler Architectural Approaches Are Better**:
* Rapid prototypes and proof-of-concept projects
* Small teams (1-3 developers) building straightforward applications
* Projects with extremely tight deadlines where architectural investment isn't justified
* Teams with strong existing architectural expertise who prefer custom solutions
## Related Documents
This document provides the foundation for architectural decision-making. For implementation details and specific guidance, explore these related resources:
***
*This document represents our current understanding of architectural tradeoffs based on real-world experience. As we continue to evolve the architecture based on feedback and new requirements, we'll update this assessment to reflect new insights and learnings.*
# UTA Architecture: Core Architecture
URL: https://dev.updatetheapp.com/docs/architecture/core-architecture
Documentation: https://dev.updatetheapp.com/docs/architecture/core-architecture
Reference guide to the core architecture of the React Native app, covering patterns, component interactions, and technology stack.
***
title: Core Architecture
description: Reference guide to the core architecture of the React Native app, covering patterns, component interactions, and technology stack.
-----------------------------------------------------------------------------------------------------------------------------------------------
# Core Architecture
## Overview
This document outlines the core architecture for our React Native mobile application. It defines the fundamental patterns, structures, and frameworks that support all features and functionality. Our architecture employs a hybrid approach combining feature-based UI organization with domain-based business logic for optimal scalability and maintainability.
## Purpose & Scope
This document provides a comprehensive reference for understanding the overall application architecture. It's intended for all developers working on the project, particularly those who need to understand how different system components interact and the distinction between shared utilities and domain-specific code.
## Core Architecture Components
### 1. Architectural Overview
QueryProvider[TanStack Query Provider]
App --> StoreProvider[State Store Provider]
App --> Navigation[Navigation Container]
QueryProvider --> Features[Feature Modules]
StoreProvider --> Features
Navigation --> Features
Features --> Domains[Domain Services]
Features --> SharedUtils[Shared Utilities]
Features --> UI[Shared UI Components]
Domains --> ApiClient[API Client]
SharedUtils --> ApiClient
Domains --> SharedUtils
ApiClient --> Backend[Backend Services]
`}
/>
Our application follows a hybrid architecture that combines:
* **Feature-based organization** for UI/UX concerns
* **Domain-based organization** for business logic and data
* **Clear separation** between shared utilities and domain-specific code
### 2. Technology Stack
| Category | Technology | Purpose |
| ------------ | ----------------------------------- | -------------------------------------------------------------------- |
| Framework | React Native | Cross-platform mobile application development |
| Language | TypeScript | Static typing and improved developer productivity |
| Server State | TanStack Query | Data fetching, caching, synchronization, and server state management |
| Client State | Zustand | Global application state management |
| Navigation | React Navigation | Screen routing and navigation management |
| HTTP Client | Axios | Promise-based HTTP requests, integrated with TanStack Query |
| Styling | React Native StyleSheet / Unistyles | Native styling with performance optimization and theme support |
| Testing | Jest & React Testing Library | Unit and integration testing |
| E2E Testing | Detox | End-to-end testing for mobile applications |
### 3. Hybrid Architecture: Features + Domains
Our architecture employs a dual approach for optimal organization:
#### Domain-Based Business Logic
* **Purpose**: Organize business logic by domain entities
* **Location**: `core/domains/[domain]/`
* **Contents**: API services, types, hooks, query keys
* **Examples**: Products, Users, Auth, Orders
#### Feature-Based UI Organization
* **Purpose**: Organize user-facing functionality by features
* **Location**: `features/[feature]/`
* **Contents**: Screens, components, navigation
* **Examples**: Shopping Cart, Product Catalog, User Profile
#### Shared Utilities
* **Purpose**: Cross-cutting concerns used everywhere
* **Location**: `core/shared/`
* **Contents**: Generic hooks, utilities, types
* **Examples**: useDebounce, formatDate, ApiResponse
### 4. Core Infrastructure
The core infrastructure is divided into domain-specific and shared components:
```
core/
├── domains/ # Domain-based business logic
│ ├── products/ # Product domain
│ │ ├── api.ts # Product API service
│ │ ├── types.ts # Product type definitions
│ │ ├── hooks.ts # Product query hooks
│ │ └── queryKeys.ts # Product query keys
│ ├── users/ # User domain
│ ├── auth/ # Authentication domain
│ │ ├── api.ts
│ │ ├── types.ts
│ │ ├── hooks.ts
│ │ ├── store.ts # Auth state management
│ │ ├── tokenService.ts # Token management
│ │ └── events.ts # Auth events
│ └── [other-domains]/
│
└── shared/ # Truly shared utilities
├── api/
│ ├── client.ts # Axios instance with interceptors
│ └── interceptors.ts # Request/response interceptors
├── query/
│ └── queryClient.ts # TanStack Query client config
├── hooks/ # Generic React hooks
├── utils/ # General utility functions
├── types/ # Cross-domain types
└── styles/ # Global styling
```
### 5. Shared vs Domain Guidelines
Clear separation between shared and domain-specific code is crucial:
#### What Goes in Shared Folders
* **core/shared/hooks/**: Generic React hooks (useDebounce, useLocalStorage)
* **core/shared/utils/**: General utilities (formatDate, capitalizeString)
* **core/shared/types/**: Cross-domain types (ApiResponse, AsyncState, ErrorResponse)
#### What Stays in Domain Folders
* **core/domains/\[domain]/hooks.ts**: Domain queries (useProducts, useUser)
* **core/domains/\[domain]/types.ts**: Domain models (Product, User)
* **core/domains/\[domain]/api.ts**: Domain services
### 6. Feature Module Structure
Each feature module is a self-contained unit focused on user-facing functionality:
```
features/
├── product-catalog/ # Product browsing feature
│ ├── components/ # Feature components
│ ├── screens/ # Screen components
│ ├── navigation.tsx # Feature navigation
│ └── state/ # Feature-specific state
└── [other-features]/ # Other feature modules
```
### 7. State Management Architecture
ServerState[Server State]
UI <--> ClientState[Client State]
subgraph TanStack Query
ServerState --> Cache[(Cache)]
Cache --> Hooks[Domain Query Hooks]
end
subgraph Zustand
ClientState <--> Stores[(State Stores)]
Stores <--> StateHooks[State Hooks]
end
Hooks --> DomainServices[Domain Services]
DomainServices --> Backend[Backend]
`}
/>
Our application clearly separates different types of state:
* **Server State**: Managed by TanStack Query through domain hooks
* **Client State**: Managed by Zustand for global application state
* **Component State**: Local state for UI-specific concerns
### 8. Navigation System
The navigation system provides type-safe routing:
* **Navigation Container**: Central React Navigation setup
* **Feature-Based Navigation**: Each feature defines its screens
* **Deep Linking**: Support for external app opening
* **Type Safety**: Full TypeScript integration
### 9. Communication Patterns
|"Domain Service"| DomainService[Domain Service]
DomainService --> ComponentB
ComponentA -->|"Events"| EventBus[Event Bus]
EventBus --> ComponentB
ComponentA -->|"Shared State"| SharedState[Shared State]
SharedState --> ComponentB
ComponentA -->|"Navigation"| Navigation[Navigation]
Navigation --> ComponentB
`}
/>
Features communicate through several patterns:
* **Domain Services**: Shared data access through domain hooks
* **Event Bus**: Publish-subscribe for loose coupling
* **Shared State**: Query cache and global state
* **Navigation**: Parameter passing between screens
### 10. Authentication Architecture
Authentication is handled as a special domain with additional concerns:
```
core/domains/auth/
├── api.ts # Auth API calls
├── types.ts # Auth types
├── hooks.ts # Auth query hooks
├── store.ts # Auth state management
├── tokenService.ts # Token storage and refresh
└── events.ts # Auth events (session expired)
```
Key features:
* Automatic token refresh through interceptors
* Session management with event-driven updates
* Secure token storage abstraction
## Design Principles
### 1. Feature-Based Modularity
We organize UI/UX by features rather than technical concerns to create clear user journey mapping and better team ownership.
**(Do ✅) Group related UI components by feature**
* This approach makes it easier to understand the complete user journey within a feature.
* Example: All components related to product browsing are grouped in `features/product-catalog/`.
**(Consider 🤔) When a feature becomes too large, consider breaking it into sub-features**
* Large features (>15-20 components) can become difficult to maintain.
* Use a subdirectory structure to organize related components within large features.
### 2. Domain-Driven Design
We organize business logic by domains for better scalability and maintainability. This separation has proven to significantly reduce complexity as applications grow.
**(Do ✅) Identify clear domain boundaries based on business entities**
* Group related business logic, types, and API calls by domain entity.
* Example: Everything related to products is in `core/domains/products/`.
**(Don't ❌) Mix domain concerns within a single domain module**
* Avoid including user-related functionality in the product domain.
* Each domain should have a single, well-defined responsibility.
### 3. Clear Separation of Concerns
Our architecture maintains clear boundaries between different responsibilities:
**(Do ✅) Use appropriate tools for different state needs**
* **Server State vs. Client State**: TanStack Query for server data, Zustand for client-only state
* **Shared vs. Domain**: Place truly shared utilities in `core/shared`, domain-specific code in domains
* **UI vs. Business Logic**: Features handle UI/UX, domains handle data and business rules
### 4. Type Safety
Comprehensive TypeScript throughout the application improves reliability and developer experience:
**(Do ✅) Define complete type interfaces for all data structures**
* **API Types**: Full request/response typing with proper documentation
* **State Types**: Typed stores and hooks for compile-time safety
* **Navigation Types**: Type-safe routes and parameters
**(Don't ❌) Use `any` types or ignore TypeScript warnings**
* Type safety is only effective when consistently applied throughout the codebase.
* If you find yourself using `any`, it might indicate a design issue that needs addressing.
### 5. Performance First
We prioritize performance considerations in our architecture design:
**(Do ✅) Implement performance optimizations from the start**
* **Optimized Rendering**: Strategic memoization of components and callbacks
* **Query Optimization**: Smart caching strategies for data fetching
* **Code Splitting**: Domain-based lazy loading for faster initial load
**(Consider 🤔) Adding performance monitoring to identify bottlenecks**
* Implement basic performance tracking to identify areas for improvement.
* Regular performance audits help maintain a fast application experience.
### 6. Developer Experience
Our architecture is designed to enhance developer productivity:
**(Do ✅) Maintain consistent patterns across the codebase**
* **Consistent Patterns**: Predictable organization makes onboarding easier
* **Clear Boundaries**: Obvious where code belongs reduces decision fatigue
* **Self-Documenting**: Structure reveals intent and guides development
## Implementation Considerations
### 1. Adding New Domains
Domains represent business entities and contain related business logic. When creating a new domain, follow these steps:
#### Create domain folder
Create a new directory in `core/domains/` with a descriptive name representing the business entity (e.g., `products`, `orders`, `payments`).
#### Add required files
At minimum, every domain needs these three files:
```typescript
// api.ts - API service methods
class NewDomainApi {
public readonly public = { /* public endpoints */ };
public readonly protected = { /* auth-required endpoints */ };
}
export const newDomainApi = new NewDomainApi();
// types.ts - TypeScript interfaces
export interface NewDomainEntity {
id: string;
// other properties
}
// hooks.ts - React Query hooks
export function useNewDomainEntities() {
return useQuery({
queryKey: ['newDomain', 'list'],
queryFn: () => newDomainApi.public.getAll()
});
}
```
#### Add optional files as needed
Consider adding these files for more complex domains:
* `queryKeys.ts` - For consistent query key management
* `events.ts` - For pub/sub communication
* `store.ts` - For domain-specific UI state
* `utils.ts` - For domain-specific utility functions
#### Create barrel exports
Add an `index.ts` file that exports the public API of your domain:
```typescript
// index.ts
export * from './api';
export * from './types';
export * from './hooks';
// export other files as needed
```
**(Do ✅) Follow the established domain structure consistently**
* A consistent structure makes the codebase more predictable and easier to navigate.
* Each domain should have the same basic file organization.
**(Don't ❌) Mix domain logic with feature-specific UI code**
* Domains should contain only business logic, data fetching, and types.
* Feature-specific UI components should stay in feature folders.
### 2. Creating New Features
Features represent user-facing functionality and organize related UI components. To create a new feature:
#### Create feature folder
Add a new directory in `features/` with a name describing the user-facing functionality (e.g., `product-catalog`, `shopping-cart`, `checkout`).
#### Add screens and components
Create subdirectories for organization:
```
features/new-feature/
├── components/ # Feature-specific components
├── screens/ # Screen components
├── navigation.tsx # Feature navigation
└── index.ts # Barrel exports
```
#### Connect to domains
Import and use domain hooks to access data and business logic:
```typescript
// features/product-catalog/screens/ProductListScreen.tsx
import { useProducts } from '@/core/domains/products';
export function ProductListScreen() {
const { data: products, isLoading } = useProducts();
// Render UI using the products data
}
```
#### Add feature-specific state if needed
For UI state that's specific to the feature but doesn't belong in a domain:
```typescript
// features/product-catalog/state/filters.ts
import { create } from 'zustand';
export const useProductFiltersStore = create((set) => ({
viewMode: 'grid',
setViewMode: (mode) => set({ viewMode: mode }),
}));
```
**(Do ✅) Keep features focused on user-facing functionality**
* Features should primarily contain UI components and layout logic.
* The API for a feature should be clean and limited to what's necessary for other parts of the app.
**(Be Aware ❗) Features can get complex - consider sub-features**
* For very large features, consider creating sub-feature folders.
* Use the same organizational patterns within sub-features.
### 3. Shared vs Domain Decision
Deciding where code belongs is critical for maintaining the architecture. Use this decision tree:
Q1{"Is it specific to\none business domain?"}
Q1 -->|"Yes"| Domain["Place in domain folder\ncore/domains/[domain-name]/"]
Q1 -->|"No"| Q2{"Is it a reusable\nutility or hook?"}
Q2 -->|"Yes"| Shared["Place in shared folder\ncore/shared/[appropriate-subfolder]/"]
Q2 -->|"No"| Q3{"Is it UI-related?"}
Q3 -->|"Yes"| UI["Is it feature-specific?"]
Q3 -->|"No"| Q4{"Is it infrastructure?"}
UI -->|"Yes"| Feature["Place in feature folder\nfeatures/[feature-name]/"]
UI -->|"No"| UILib["Place in UI library\nui/[component-type]/"]
Q4 -->|"Yes"| Config["Place in app config\ncore/config/"]
Q4 -->|"No"| Reconsider["Reconsider the design"]
classDef decision fill:#f9f,stroke:#333;
classDef location fill:#bfb,stroke:#333;
classDef reconsider fill:#fbb,stroke:#333;
class Q1,Q2,Q3,Q4,UI decision;
class Domain,Shared,Feature,UILib,Config location;
class Reconsider reconsider;
`}
/>
**(Do ✅) Ask critical questions when deciding placement**
* "Is this specific to one business domain?" → Domain folder
* "Is it a reusable utility used across domains?" → Shared folder
* "Is it a UI component specific to one feature?" → Feature folder
**(Don't ❌) Create ambiguous or overlapping responsibilities**
* Avoid creating utilities that do the same thing in different places.
* Don't add business logic to UI components.
### 4. Cross-Feature Communication
Features should never directly import from other features. Instead, use these patterns:
**(Do ✅) Use domain hooks for shared data access**
```typescript
// Both features use the same domain hook
// features/product-catalog/screens/ProductList.tsx
// features/product-detail/screens/ProductDetail.tsx
import { useProduct } from '@/core/domains/products';
```
**(Do ✅) Use events for loose coupling**
```typescript
// features/shopping-cart/screens/CartScreen.tsx
import { cartEvents } from '@/core/domains/cart';
// Emit event when item added
cartEvents.emit('cart:itemAdded', { productId, quantity });
// features/product-detail/components/AddedNotification.tsx
// Listen for cart events
cartEvents.on('cart:itemAdded', handleItemAdded);
```
**(Do ✅) Pass parameters through navigation**
```typescript
// features/product-catalog/components/ProductCard.tsx
import { useNavigation } from '@react-navigation/native';
const navigation = useNavigation();
navigation.navigate('ProductDetail', { productId: product.id });
```
**(Don't ❌) Import directly between features**
```typescript
// AVOID THIS - creates tight coupling
import { ProductCard } from '@/features/product-catalog/components/ProductCard';
```
**(Consider 🤔) Using Signals for real-time updates**
* For real-time updates that need to flow across features, consider using a signals library or reactive state.
* This provides a performance benefit for frequently changing values.
## Related Documents
* [Project Structure Reference](/docs/architecture/project-structure)
* [Domain Architecture Guide](/docs/architecture/domain-architecture)
* [Shared Utilities Guide](/docs/features/shared-utilities)
* [File Naming Conventions](/docs/coding-standards/file-naming-conventions)
* [Communication Patterns](/docs/features/communication-patterns)
* [Authentication Architecture](/docs/api/authentication)
* [State Management](/docs/state-management)
* [API Architecture](/docs/api)
# UTA Architecture: Project Structure
URL: https://dev.updatetheapp.com/docs/architecture/project-structure
Documentation: https://dev.updatetheapp.com/docs/architecture/project-structure
Comprehensive reference for the project's directory structure, file organization, and module boundaries with clear shared/domain separation.
***
title: Project Structure
description: Comprehensive reference for the project's directory structure, file organization, and module boundaries with clear shared/domain separation.
---------------------------------------------------------------------------------------------------------------------------------------------------------
# Project Structure
## Overview
This document provides a comprehensive reference for the project's directory structure, file organization, and module boundaries. It outlines how code is organized following a hybrid architecture that combines feature-based UI organization with domain-based business logic, emphasizing the clear separation between shared utilities and domain-specific code.
## Purpose & Scope
This reference is intended for all developers working on the project to understand how code should be organized and where different types of code should be placed. It serves as the definitive guide for project organization with clear rules for shared versus domain-specific code placement.
## Directory Structure
### Root Structure
```
project-root/
├── src/ # Source code
│ ├── core/ # Domain logic & shared utilities
│ ├── features/ # Feature modules (UI/UX)
│ ├── ui/ # Shared UI components
│ ├── navigation/ # Navigation configuration
│ ├── App.tsx # Application entry with providers
│ └── index.js # Root file
├── assets/ # Static assets (project root level)
├── android/ # Android platform files
├── ios/ # iOS platform files
└── package.json # Project configuration
```
#### High-Level Architecture Relationships
Core
Features --> UI
Features --> Nav
App --> Features
App --> Core
App --> UI
App --> Nav
style Features fill:#f9f,stroke:#333
style Core fill:#bfb,stroke:#333
style UI fill:#bbf,stroke:#333
style Nav fill:#fbb,stroke:#333
`}
/>
### Core Directory
The `core` directory is divided into two main sections: domain-based business logic and truly shared utilities:
domain_internals
style domains fill:#f9f,stroke:#333
style shared fill:#bfb,stroke:#333
style domain_internals fill:#ddd,stroke:#333
`}
/>
```
core/
├── domains/ # Domain-based business logic
│ ├── products/ # Product domain
│ │ ├── api.ts # Product API service
│ │ ├── types.ts # Product types (Product, ProductCategory)
│ │ ├── hooks.ts # Product hooks (useProducts, useProduct)
│ │ └── queryKeys.ts # Product query keys
│ │
│ ├── users/ # User domain
│ │ ├── api.ts # User API service
│ │ ├── types.ts # User types (User, UserProfile)
│ │ ├── hooks.ts # User hooks (useUser, useUpdateUser)
│ │ └── queryKeys.ts # User query keys
│ │
│ ├── auth/ # Authentication domain
│ │ ├── api.ts # Auth API service
│ │ ├── types.ts # Auth types (AuthState, LoginRequest)
│ │ ├── hooks.ts # Auth hooks (useAuth, useLogin)
│ │ ├── store.ts # Auth state management
│ │ ├── tokenService.ts # Token management service
│ │ ├── events.ts # Auth events (sessionExpired)
│ │ └── queryKeys.ts # Auth query keys
│ │
│ └── [other-domains]/ # Additional domains (orders, payments, etc.)
│
└── shared/ # Truly shared, non-domain utilities
├── api/
│ ├── client.ts # Axios instance with interceptors
│ └── interceptors.ts # Request/response interceptors
│
├── query/
│ └── queryClient.ts # TanStack Query client config
│
├── hooks/ # Generic React hooks (NO domain logic)
│ ├── useDebounce.ts # Debounce hook
│ ├── useLocalStorage.ts # Local storage hook
│ ├── useMediaQuery.ts # Media query hook
│ └── useTimeout.ts # Timeout hook
│
├── utils/ # General utilities (NO domain logic)
│ ├── date.ts # Date formatting utilities
│ ├── string.ts # String manipulation utilities
│ ├── validation.ts # Generic validation helpers
│ └── number.ts # Number formatting utilities
│
├── types/ # Cross-domain types
│ ├── ui.ts # Cross-domain UI patterns (BaseComponentProps, LoadingState)
│ ├── global.ts # Global app types (AppState, Theme)
│ └── api.ts # Generic API types (ErrorResponse, Pagination)
│
└── styles/ # Global styling
├── theme.ts # Theme constants (colors, spacing)
├── typography.ts # Text styles and fonts
├── layout.ts # Layout utilities
├── shadows.ts # Shadow styles
└── mixins.ts # Reusable style patterns
```
For detailed information on domain-based business logic organization and implementation patterns, see the [Domain Architecture Guide](/docs/architecture/domain-architecture).
### Features Directory
Features contain UI/UX code organized by user-facing functionality:
```
features/
├── product-catalog/ # Product browsing feature
│ ├── components/ # Feature-specific components
│ │ └── ProductCard/
│ │ ├── ProductCard.tsx
│ │ ├── styles.ts
│ │ └── index.ts
│ ├── screens/ # Feature screens
│ │ └── ProductListScreen/
│ │ ├── ProductListScreen.tsx
│ │ ├── styles.ts
│ │ └── index.ts
│ ├── navigation.tsx # Feature navigation setup
│ └── state/ # Feature-specific UI state
│ └── filterStore.ts # UI filters state
│
├── shopping-cart/ # Shopping cart feature
│ ├── components/
│ ├── screens/
│ ├── navigation.tsx
│ └── state/
│ └── cartUIStore.ts # Cart UI state (drawer open/close)
│
└── [other-features]/ # Additional features
```
For more information on implementing features and communication between features and domains, see the [Communication Patterns](/docs/features/communication-patterns) guide.
### UI Directory
Shared UI components used across features:
```
ui/
├── Button/ # Generic button component
│ ├── Button.tsx
│ ├── styles.ts
│ └── index.ts
├── Card/ # Generic card component
│ ├── Card.tsx
│ ├── styles.ts
│ └── index.ts
├── Input/ # Generic input component
│ ├── Input.tsx
│ ├── styles.ts
│ └── index.ts
└── [other-components]/ # Other shared UI components
```
For UI component best practices and implementation details, refer to the UI Development guides.
### Navigation Directory
Navigation configuration for the entire app:
```
navigation/
├── RootNavigator.tsx # Main navigation setup
├── AuthNavigator.tsx # Auth flow navigation
├── MainNavigator.tsx # Main app navigation
└── types.ts # Navigation type definitions
```
For more information on navigation configuration and routing strategies, see the related sections in the [Core Architecture Reference](/docs/architecture/core-architecture).
### Assets Directory
Static assets organization for brand assets, fonts, animations, and other media:
```
assets/ # Project root level
├── brand/ # Brand assets (logos, illustrations)
│ ├── logos/ # Company/product logos
│ │ ├── company-logo.svg # SVG for scalability
│ │ ├── product-logo.svg # SVG for scalability
│ │ └── partner-logo.png # PNG with @2x, @3x variants
│ ├── illustrations/ # Marketing/onboarding graphics
│ │ ├── onboarding/
│ │ └── empty-states/
│ └── backgrounds/ # Hero banners, splash screens
├── fonts/ # Custom font files
│ ├── OpenSans-Regular.ttf
│ ├── OpenSans-Bold.ttf
│ └── OpenSans-Italic.ttf
└── animations/ # Lottie and other animations
└── loading.json # Loading animation
```
**Important**: UI icons (navigation, actions, status) are managed separately in the UI component system. See the [Icon Management Guide](/docs/ui-development/assets/icon-management) for UI icons and the [Assets Management Guide](/docs/ui-development/assets/assets-management) for brand assets.
### Reference Implementations Directory
Working examples and demonstrations that mirror the main project structure:
|"mirrors"| ref
MainCore -.->|"examples"| RefCore
MainFeatures -.->|"patterns"| RefFeatures
MainUI -.->|"components"| RefUI
style main fill:#f9f,stroke:#333
style ref fill:#bfb,stroke:#333
`}
/>
```
reference-implementations/ # Project root level
├── README.md # Overview and usage guide
├── core/ # Domain and shared utilities examples
│ ├── domains/ # Business logic implementations
│ │ ├── products/ # Product domain examples
│ │ │ ├── api.example.ts # Product API service patterns
│ │ │ ├── hooks.example.ts # Product hooks implementations
│ │ │ ├── types.example.ts # Product type definitions
│ │ │ └── README.md # Domain implementation guide
│ │ ├── users/ # User domain examples
│ │ ├── auth/ # Authentication examples
│ │ └── [other-domains]/ # Additional domain examples
│ │
│ └── shared/ # Shared utilities examples
│ ├── api/ # API client patterns
│ │ ├── client.example.ts # Configured API client
│ │ ├── interceptors.example.ts # Request/response handling
│ │ └── README.md # API integration guide
│ ├── hooks/ # Generic hook implementations
│ │ ├── useDebounce.example.ts # Debounce hook pattern
│ │ ├── useLocalStorage.example.ts # Storage hook pattern
│ │ └── README.md # Hook development guide
│ ├── utils/ # Utility function examples
│ │ ├── validation.example.ts # Validation patterns
│ │ ├── formatting.example.ts # Data formatting
│ │ └── README.md # Utility development guide
│ └── types/ # Cross-domain type examples
│ ├── api.example.ts # Generic API types
│ ├── ui.example.ts # UI pattern types
│ └── README.md # Type architecture guide
│
├── features/ # Feature implementation examples
│ ├── product-catalog/ # Product browsing feature example
│ │ ├── components/ # Feature-specific components
│ │ ├── screens/ # Feature screen implementations
│ │ ├── navigation.example.tsx # Feature navigation patterns
│ │ ├── state.example.ts # Feature state management
│ │ └── README.md # Feature implementation guide
│ ├── shopping-cart/ # Shopping cart feature example
│ ├── user-authentication/ # Auth flow feature example
│ └── [other-features]/ # Additional feature patterns
│
├── ui/ # UI component implementations
│ ├── foundation/ # Basic building blocks
│ │ ├── Button/ # Button component examples
│ │ │ ├── Button.example.tsx # Main implementation
│ │ │ ├── Button.variants.tsx # All visual variants
│ │ │ ├── Button.playground.tsx # Interactive examples
│ │ │ ├── Button.test.tsx # Testing patterns
│ │ │ └── README.md # Component guide
│ │ ├── Input/ # Input component examples
│ │ ├── Card/ # Card component examples
│ │ └── [other-foundation]/ # Additional foundation components
│ │
│ ├── patterns/ # Composed UI patterns
│ │ ├── Modal/ # Modal pattern implementations
│ │ │ ├── Modal.example.tsx # Basic modal usage
│ │ │ ├── Modal.complex.tsx # Advanced patterns
│ │ │ ├── Modal.accessibility.tsx # A11y implementation
│ │ │ └── README.md # Pattern guide
│ │ ├── Form/ # Form pattern examples
│ │ ├── DataTable/ # Data table implementations
│ │ └── [other-patterns]/ # Additional pattern components
│ │
│ └── business/ # Domain-aware components
│ ├── ProductCard/ # Product display examples
│ │ ├── ProductCard.example.tsx # Basic implementation
│ │ ├── ProductCard.loading.tsx # Loading states
│ │ ├── ProductCard.error.tsx # Error handling
│ │ └── README.md # Business component guide
│ ├── UserAvatar/ # User display examples
│ └── [other-business]/ # Additional business components
│
└── docs/ # Documentation examples
├── guides/ # Implementation guides
├── patterns/ # Code pattern examples
└── best-practices/ # Best practice implementations
```
#### Purpose and Benefits
**Documentation → Examples → Implementation Flow**:
1. **Learn**: Read comprehensive documentation
2. **Study**: Examine working reference implementations
3. **Implement**: Build production features with confidence
**Key Benefits**:
* **Working Code**: All examples are tested and functional
* **Mirror Structure**: 1:1 mapping with main project architecture
* **Progressive Learning**: From simple examples to complex patterns
* **Template Ready**: Copy-paste starting points for new features
* **Best Practices**: Demonstrates recommended patterns and approaches
**Cross-References**: Each implementation includes:
* Links back to relevant documentation sections
* GitHub source links for the actual production code
* Testing examples and patterns
* Performance considerations
* Accessibility implementations
For detailed information on using reference implementations effectively, see the [Reference Implementation Guide](/docs/reference-implementations).
## Module Boundaries
### Shared vs Domain Rules
useDebounce, useLocalStorage]
SharedUtils[Generic Utils formatDate, capitalizeString]
SharedTypes[Cross-domain Types ApiResponse, AsyncState]
end
subgraph Domains[core/domains - Business Logic]
ProductDomain[products/ API, types, hooks]
UserDomain[users/ API, types, hooks]
AuthDomain[auth/ API, types, hooks, store]
end
subgraph Features[features/ - UI Layer]
ProductCatalog[product-catalog]
ShoppingCart[shopping-cart]
end
Features --> Shared
Features --> Domains
Domains --> Shared
Domains -.->|"Allowed but minimize"| Domains
Features -.-|"❌ NEVER"| Features
style Domains fill:#f9f,stroke:#333,stroke-width:2px
style Shared fill:#9ff,stroke:#333,stroke-width:2px
style Features fill:#ff9,stroke:#333,stroke-width:2px
`}
/>
### Import Rules
**Why Import Rules Matter**
Clear import boundaries help maintain a clean architecture by preventing circular dependencies,
ensuring proper separation of concerns, and making the codebase more maintainable and testable.
#### Allowed Import Patterns
* **Features** can import from:
* Core domains (`core/domains/*`) for business logic
* Shared utilities (`core/shared/*`) for common functions
* UI components (`ui/*`) for shared interface elements
* Navigation (`navigation/*`) for routing functions
* **Core domains** can import from:
* Shared utilities (`core/shared/*`) only
* **UI components** can import from:
* Shared utilities (`core/shared/*`) only
* **Navigation** can import from:
* Shared utilities (`core/shared/*`) only
#### Prohibited Import Patterns
* **Features** cannot import from:
* Other features (to prevent feature coupling)
* **Core domains** cannot import from:
* Features (to prevent business logic depending on UI)
* Other domains (to maintain domain independence)
* **Shared utilities** cannot import from:
* Features (to maintain utility independence)
* Domains (to keep utilities truly shared and reusable)
|✓ can import| Domains
Features -->|✓ can import| Shared
Features -->|✓ can import| UI
Features -->|✓ can import| Navigation
Domains -->|✓ can import| Shared
UI -->|✓ can import| Shared
Navigation -->|✓ can import| Shared
end
subgraph Prohibited[Prohibited Import Patterns]
Features2[features/*]
Domains2[core/domains/*]
Shared2[core/shared/*]
Features2 -->|❌ cannot import| Features2
Domains2 -->|❌ cannot import| Features2
Domains2 -->|❌ cannot import| Domains2
Shared2 -->|❌ cannot import| Domains2
Shared2 -->|❌ cannot import| Features2
end
style Allowed fill:#f8f8f8,stroke:#333
style Prohibited fill:#fff8f8,stroke:#333
style Features fill:#f9f,stroke:#333
style UI fill:#bbf,stroke:#333
style Navigation fill:#fbb,stroke:#333
style Domains fill:#bfb,stroke:#333
style Shared fill:#dfd,stroke:#333
style Features2 fill:#f9f,stroke:#333
style Domains2 fill:#bfb,stroke:#333
style Shared2 fill:#dfd,stroke:#333
`}
/>
#### Common Import Violations to Avoid
* **Cross-feature imports**: Features should not directly import from each other
* **Domain-to-domain imports**: Domains should remain independent of each other
* **Shared importing domains**: Shared utilities must not depend on domain-specific code
* **Circular dependencies**: No module should directly or indirectly depend on itself
### Decision Tree for Code Placement
```
Is this code specific to one business domain?
├─ YES → Place in core/domains/[domain]/
│ ├─ API calls? → api.ts
│ ├─ Data types? → types.ts
│ ├─ React hooks? → hooks.ts
│ └─ Query keys? → queryKeys.ts
│
└─ NO → Is it used across multiple domains?
├─ YES → Place in core/shared/
│ ├─ React hook? → shared/hooks/
│ ├─ Utility function? → shared/utils/
│ └─ Type definition? → shared/types/
│
└─ NO → Is it UI-specific?
├─ YES → Place in features/[feature]/
└─ NO → Reconsider architecture
```
## Component Structure
All components follow this consistent structure:
```
ComponentName/
├── ComponentName.tsx # Component implementation
├── styles.ts # Component-specific styles
├── types.ts # Component-specific types (optional)
├── __tests__/ # Tests directory
│ └── ComponentName.test.tsx # Unit tests
└── index.ts # Exports component
```
## File Naming Conventions
This project follows strict file naming conventions to ensure consistency:
* **Components**: PascalCase (e.g., `Button.tsx`, `ProductCard.tsx`)
* **Hooks**: camelCase with "use" prefix (e.g., `useAuth.ts`, `useDebounce.ts`)
* **Domain files**: lowercase descriptive (e.g., `api.ts`, `types.ts`)
* **Utilities**: lowercase by category (e.g., `date.ts`, `string.ts`)
* **Features**: kebab-case folders (e.g., `product-catalog/`)
* **Tests**: `.test.ts` or `.test.tsx` suffix
For complete naming guidelines, see the [File Naming Conventions](/docs/coding-standards/file-naming-conventions).
## Do's and Don'ts
### Shared Folders Guidelines
* (Do ✅) Place **generic, reusable** hooks like `useDebounce` in `core/shared/hooks/`
* (Don't ❌) Put **domain-specific** hooks like `useProducts` in shared hooks
* (Do ✅) Keep **general utilities** like `formatDate` in `core/shared/utils/`
* (Don't ❌) Put **domain utilities** like `calculateProductDiscount` in shared utils
* (Do ✅) Store **cross-domain types** like `ApiResponse` and `AsyncState` in `core/shared/types/`
* (Don't ❌) Put **component-specific types** like `ButtonProps` in shared types - these belong co-located with components per the [Feature Implementation Decision Tree](/docs/guides/feature-implementation-decision-tree)
* (Don't ❌) Duplicate **domain types** like `Product` in shared types
### Domain Folders Guidelines
* (Do ✅) Keep **domain queries** like `useProducts` in `core/domains/products/hooks.ts`
* (Do ✅) Store **domain models** like `Product` in `core/domains/products/types.ts`
* (Do ✅) Place **domain API calls** in `core/domains/products/api.ts`
* (Consider 🤔) Creating `utils.ts` in domain if needed for domain-specific helpers
* (Consider 🤔) Adding `queryKeys.ts` only if you have > 3 query keys
### Feature Folders Guidelines
* (Do ✅) Import data from **domain hooks** (`core/domains/*/hooks.ts`)
* (Do ✅) Use **shared utilities** from `core/shared/*`
* (Do ✅) Keep **UI state** local to features
* (Don't ❌) Import from **other features**
* (Don't ❌) Implement **business logic** in features
### General Guidelines
* (Do ✅) Use **index.ts** files for clean exports
* (Do ✅) Maintain **consistent naming** conventions
* (Do ✅) Write **tests** alongside components
* (Don't ❌) Create **circular dependencies**
* (Don't ❌) Mix **concerns** (UI with business logic)
## Examples
### Correct Import Patterns
```typescript
// In features/product-catalog/screens/ProductList.tsx
import { useProducts } from '@/core/domains/products'; // ✅ Domain hook
import { formatCurrency } from '@/core/shared/utils'; // ✅ Shared utility
import { Button } from '@/ui/foundation/Button'; // ✅ Shared UI component
// In core/domains/products/api.ts
import { apiClient } from '@/core/shared/api'; // ✅ Shared API client
import type { Product } from './types'; // ✅ Same domain type
// In core/shared/hooks/useDebounce.ts
import { useState, useEffect } from 'react'; // ✅ Only React imports
// No domain imports! ✅
```
### Incorrect Import Patterns
```typescript
// In features/product-catalog/screens/ProductList.tsx
import { CartItem } from '@/features/shopping-cart/components'; // ❌ Cross-feature
// In core/shared/utils/product.ts
import { Product } from '@/core/domains/products'; // ❌ Domain in shared
// In core/domains/products/hooks.ts
import { useDebounce } from '@/core/shared/hooks'; // ✅ Fixed: Use shared hook
```
## Implementation Considerations
### Adding New Domains
1. Create folder: `core/domains/[domain-name]/`
2. Add required files:
* `api.ts` - API service methods
* `types.ts` - TypeScript interfaces
* `hooks.ts` - Query/mutation hooks
3. Add optional files as needed:
* `queryKeys.ts` - If many query keys
* `store.ts` - If client state needed
* `events.ts` - If event system needed
4. Update barrel exports
### Adding New Features
1. Create folder: `features/[feature-name]/`
2. Add standard structure:
* `screens/` - Feature screens
* `components/` - Feature components
* `navigation.tsx` - Feature routing
3. Import from domains for data
4. Keep business logic in domains
### Migration Strategy
When refactoring existing code:
1. **Identify domain boundaries**
* Group related API calls
* Extract business types
2. **Move to appropriate locations**
* Domain logic → `core/domains/`
* Generic utilities → `core/shared/`
* UI components → Features or `ui/`
3. **Update imports**
* Fix all import paths
* Remove circular dependencies
4. **Test thoroughly**
* Ensure no regressions
* Verify module boundaries
## Related Documents
* [](/docs/architecture/core-architecture)
* [](/docs/architecture/domain-architecture)
* [](/docs/features/shared-utilities)
* [](/docs/coding-standards/file-naming-conventions)
* [](/docs/features/communication-patterns)
* [](/docs/ui-development/assets)
# UTA Architecture: Domain Architecture Guide
URL: https://dev.updatetheapp.com/docs/architecture/domain-architecture
Documentation: https://dev.updatetheapp.com/docs/architecture/domain-architecture
Guide to implementing domain-based architecture for business logic organization.
***
title: Domain Architecture Guide
description: Guide to implementing domain-based architecture for business logic organization.
---------------------------------------------------------------------------------------------
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
# Domain Architecture Guide
## Overview
This guide explains our approach to domain-based architecture, which we've implemented in the `core/domains/` directory. In our experience, organizing business logic by entity (such as products, users, authentication) rather than by technical concern creates clearer boundaries and significantly improves scalability as applications grow.
Domain-based architecture helps solve several common challenges in React Native development:
* **Code organization** as applications scale beyond a handful of features
* **Maintainability** by creating logical boundaries between business entities
* **Team collaboration** by enabling parallel work with minimal conflicts
* **Testing** by isolating business logic from UI concerns
By following these patterns, we've found teams can work more efficiently and produce more robust applications.
## Purpose & Scope
This document is designed to help developers at all experience levels:
* When and why to create separate domains for business logic
* How to structure domain folders for consistency and discoverability
* Patterns for implementing domain services, hooks, and types
* Best practices for maintaining proper boundaries between domains
* Techniques for cross-domain communication without tight coupling
* **New team members** wanting to understand our architecture
* **Feature developers** needing to integrate with domain services
* **Domain maintainers** responsible for implementing business logic
* **Technical leads** making architectural decisions
To get the most from this guide, you should be familiar with:
* Basic React and React Native concepts
* TypeScript fundamentals (interfaces, types)
* [Core Architecture Reference](/docs/architecture/core-architecture) (recommended first)
* [API Integration](/docs/api) (helpful for understanding API services)
## Domain Boundaries
### Key Principles
1. **Domain Independence**: Domains should be self-contained and not directly import from each other
2. **Shared Interface**: Domains expose public APIs through hooks and types
3. **Feature Integration**: Features compose multiple domains for UI
ProductDomain
ShoppingCart --> ProductDomain
ShoppingCart --> CartDomain
Checkout --> CartDomain
Checkout --> UserDomain
Checkout --> OrderDomain
style Features fill:#f8f8f8,stroke:#333
style Domains fill:#f0f0f0,stroke:#333
style ProductList fill:#bbf,stroke:#333
style ShoppingCart fill:#bbf,stroke:#333
style Checkout fill:#bbf,stroke:#333
style ProductDomain fill:#bfb,stroke:#333
style CartDomain fill:#bfb,stroke:#333
style UserDomain fill:#bfb,stroke:#333
style OrderDomain fill:#bfb,stroke:#333
`}
/>
### Cross-Domain Communication
OrdersHooks
FeatureUI --> ProductsHooks
FeatureUI --> UserHooks
%% Domain-to-domain communication
OrdersHooks --> ProductsAPI
OrdersHooks --> UserAPI
%% Event-based communication
AuthEvents --> Orders
AuthEvents --> Products
AuthEvents --> User
style FeatureUI fill:#bbf,stroke:#333
style OrdersHooks fill:#f9f,stroke:#333
style ProductsHooks fill:#f9f,stroke:#333
style UserHooks fill:#f9f,stroke:#333
style OrdersAPI fill:#bfb,stroke:#333
style ProductsAPI fill:#bfb,stroke:#333
style UserAPI fill:#bfb,stroke:#333
style AuthEvents fill:#fbb,stroke:#333
style AuthAPI fill:#bfb,stroke:#333
`}
/>
#### Communication Patterns
1. **Direct Hook Usage**: Features import and use hooks from multiple domains simultaneously
2. **Cross-Domain API Calls**: Domain services can access other domain services through their public APIs
3. **Event-Based Communication**: Domains can broadcast events that other domains listen for
4. **Shared State Access**: Domains can share data through centralized state stores
## What is a Domain?
A domain represents a distinct business entity or area of functionality:
* **Products**: Everything related to product data and operations
* **Users**: User management and profile functionality
* **Auth**: Authentication and authorization logic
* **Orders**: Order processing and management
* **Payments**: Payment processing logic
## Domain Structure
### Standard Domain Anatomy
Every domain follows this structure:
```
core/domains/[domain-name]/
├── api.ts # API service methods (required)
├── types.ts # TypeScript interfaces (required)
├── hooks.ts # React Query hooks (required)
├── queryKeys.ts # Query key factory (optional)
├── store.ts # Client state if needed (optional)
├── events.ts # Event definitions (optional)
├── utils.ts # Domain utilities (optional)
└── index.ts # Barrel exports (required)
```
API
Index --> Types
Index --> Hooks
Index --> QueryKeys
Index --> Store
Index --> Events
Index --> Utils
Hooks --> API
Hooks --> QueryKeys
Hooks --> Types
API --> Types
classDef required fill:#f9f,stroke:#333
classDef optional fill:#bfb,stroke:#333
class API,Types,Hooks,Index required
class QueryKeys,Store,Events,Utils optional
`}
/>
### Required Files
All domain files follow our [File Naming Conventions](/docs/coding-standards/file-naming-conventions). Each file has a specific purpose:
#### 1. `api.ts` - API Service Methods
Contains all API calls for the domain, following the **mandatory** public/protected pattern:
```typescript
// core/domains/products/api.ts
import { publicApi, authenticatedApi } from '@/core/shared/api/client';
import type { Product, CreateProductDto, UpdateProductDto } from './types';
class ProductApi {
// Public endpoints (no auth required)
public readonly public = {
getAll: async (): Promise => {
const { data } = await publicApi.get('/products');
return data;
},
getById: async (id: string): Promise => {
const { data } = await publicApi.get(`/products/${id}`);
return data;
},
search: async (query: string): Promise => {
const { data } = await publicApi.get('/products/search', {
params: { q: query }
});
return data;
},
};
// Protected endpoints (auth required)
public readonly protected = {
create: async (product: CreateProductDto): Promise => {
const { data } = await authenticatedApi.post('/products', product);
return data;
},
update: async (id: string, updates: UpdateProductDto): Promise => {
const { data } = await authenticatedApi.put(`/products/${id}`, updates);
return data;
},
delete: async (id: string): Promise => {
await authenticatedApi.delete(`/products/${id}`);
},
getUserProducts: async (userId: string): Promise => {
const { data } = await authenticatedApi.get(`/users/${userId}/products`);
return data;
},
};
}
export const productApi = new ProductApi();
```
**This pattern is required for all domain APIs**. Benefits:
* Clear separation of public vs authenticated endpoints
* No runtime flags or configuration needed
* Type-safe API calls
* Better developer experience with IntelliSense
PublicClient
Protected --> AuthClient
PublicClient --> Backend
AuthClient --> Backend
Backend[Backend API]
style Public fill:#bfb,stroke:#333
style Protected fill:#f9f,stroke:#333
`}
/>
#### 2. `types.ts` - TypeScript Interfaces
Defines all types for the domain:
```typescript
// core/domains/products/types.ts
export interface Product {
id: string;
name: string;
description: string;
price: number;
category: string;
inStock: boolean;
createdAt: Date;
updatedAt: Date;
}
export interface CreateProductDto {
name: string;
description: string;
price: number;
category: string;
inStock?: boolean;
}
export interface UpdateProductDto {
name?: string;
description?: string;
price?: number;
category?: string;
inStock?: boolean;
}
export interface ProductFilters {
category?: string;
minPrice?: number;
maxPrice?: number;
inStock?: boolean;
}
```
#### 3. `hooks.ts` - React Query Hooks
Implements data fetching hooks using TanStack Query, following the public/protected pattern:
```typescript
// core/domains/products/hooks.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { productApi } from './api';
import { productQueryKeys } from './queryKeys';
import type { Product, CreateProductDto, UpdateProductDto, ProductFilters } from './types';
// Public query hooks (no auth required)
export function useProducts(filters?: ProductFilters) {
return useQuery({
queryKey: productQueryKeys.list(filters),
queryFn: () => productApi.public.getAll(filters),
});
}
export function useProduct(id: string) {
return useQuery({
queryKey: productQueryKeys.detail(id),
queryFn: () => productApi.public.getById(id),
enabled: !!id,
});
}
export function useProductSearch(query: string) {
return useQuery({
queryKey: productQueryKeys.search(query),
queryFn: () => productApi.public.search(query),
enabled: query.length > 2,
});
}
// Protected mutation hooks (auth required)
export function useCreateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: productApi.protected.create,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: productQueryKeys.lists()
});
},
});
}
export function useUpdateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, updates }: { id: string; updates: UpdateProductDto }) =>
productApi.protected.update(id, updates),
onSuccess: (data, { id }) => {
queryClient.setQueryData(productQueryKeys.detail(id), data);
queryClient.invalidateQueries({
queryKey: productQueryKeys.lists()
});
},
});
}
export function useDeleteProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: productApi.protected.delete,
onSuccess: (_, id) => {
queryClient.removeQueries({
queryKey: productQueryKeys.detail(id)
});
queryClient.invalidateQueries({
queryKey: productQueryKeys.lists()
});
},
});
}
// Protected query hooks (auth required)
export function useUserProducts(userId: string) {
return useQuery({
queryKey: productQueryKeys.userProducts(userId),
queryFn: () => productApi.protected.getUserProducts(userId),
enabled: !!userId,
});
}
```
#### Domain Data Flow Pattern
Hooks
Hooks --> API
API --> Backend
Backend --> API
API --> Hooks
Hooks --> UI
style UI fill:#bbf,stroke:#333
style Hooks fill:#f9f,stroke:#333
style API fill:#bfb,stroke:#333
style Backend fill:#fbb,stroke:#333
`}
/>
#### Data Flow Steps
1. UI Component calls domain hook (e.g., `useProducts()`)
2. Domain hook calls API service
3. API service makes HTTP request to backend
4. Backend responds with data
5. API service returns typed data to hook
6. Hook returns cached data to UI
### Optional Files
#### 4. `queryKeys.ts` - Query Key Factory
Use when you have > 3 query keys:
```typescript
// core/domains/products/queryKeys.ts
export const productQueryKeys = {
all: ['products'] as const,
lists: () => [...productQueryKeys.all, 'list'] as const,
list: (filters?: ProductFilters) =>
[...productQueryKeys.lists(), { filters }] as const,
details: () => [...productQueryKeys.all, 'detail'] as const,
detail: (id: string) => [...productQueryKeys.details(), id] as const,
byCategory: (category: string) =>
[...productQueryKeys.all, 'category', category] as const,
};
```
#### 5. `store.ts` - Client State
For domain-specific UI state:
```typescript
// core/domains/products/store.ts
import { create } from 'zustand';
interface ProductsUIState {
selectedCategory: string | null;
viewMode: 'grid' | 'list';
sortBy: 'name' | 'price' | 'date';
setSelectedCategory: (category: string | null) => void;
setViewMode: (mode: 'grid' | 'list') => void;
setSortBy: (sort: 'name' | 'price' | 'date') => void;
}
export const useProductsUIStore = create((set) => ({
selectedCategory: null,
viewMode: 'grid',
sortBy: 'name',
setSelectedCategory: (category) => set({ selectedCategory: category }),
setViewMode: (mode) => set({ viewMode: mode }),
setSortBy: (sort) => set({ sortBy: sort }),
}));
```
#### 6. `events.ts` - Event Definitions
For pub/sub communication:
```typescript
// core/domains/products/events.ts
import { EventEmitter } from '@/core/shared/utils/events';
export type ProductEvents = {
'product:created': { productId: string };
'product:updated': { productId: string; changes: Record };
'product:deleted': { productId: string };
'product:stockLow': { productId: string; quantity: number };
};
export const productEvents = new EventEmitter();
// Usage example
productEvents.on('product:stockLow', ({ productId, quantity }) => {
console.log(`Product ${productId} has low stock: ${quantity}`);
});
```
#### 7. `index.ts` - Barrel Exports
Clean public API for the domain:
```typescript
// core/domains/products/index.ts
export * from './api';
export * from './types';
export * from './hooks';
export * from './queryKeys';
export { productEvents } from './events';
export { useProductsUIStore } from './store';
```
## Real-World Examples
### Authentication Domain
The auth domain has special requirements:
```typescript
// core/domains/auth/tokenService.ts
import { storage } from '@/core/shared/utils/storage';
export const tokenService = {
getAccessToken: () => storage.get('accessToken'),
getRefreshToken: () => storage.get('refreshToken'),
setTokens: (access: string, refresh: string) => {
storage.set('accessToken', access);
storage.set('refreshToken', refresh);
},
clearTokens: () => {
storage.remove('accessToken');
storage.remove('refreshToken');
},
};
```
```typescript
// core/domains/auth/events.ts
import { EventEmitter } from '@/core/shared/utils/events';
export type AuthEvents = {
'auth:login': { userId: string };
'auth:logout': void;
'auth:sessionExpired': void;
'auth:tokenRefreshed': { accessToken: string };
};
export const authEvents = new EventEmitter();
```
### User Domain
```typescript
// core/domains/users/hooks.ts
export function useCurrentUser() {
return useQuery({
queryKey: userQueryKeys.current(),
queryFn: userApi.getCurrentUser,
});
}
export function useUpdateProfile() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (updates: UpdateProfileDto) =>
userApi.updateProfile(updates),
onSuccess: (data) => {
queryClient.setQueryData(userQueryKeys.current(), data);
},
});
}
```
## Communication Between Domains
### Preferred: Through Shared Cache
```typescript
// In products domain
export function useProductWithUser(productId: string) {
const { data: product } = useProduct(productId);
const { data: user } = useUser(product?.ownerId);
return { product, owner: user };
}
```
### Alternative: Through Events
```typescript
// In orders domain
import { productEvents } from '@/core/domains/products';
export function useCreateOrder() {
return useMutation({
mutationFn: orderApi.create,
onSuccess: (order) => {
// Notify product domain about stock changes
order.items.forEach(item => {
productEvents.emit('product:stockLow', {
productId: item.productId,
quantity: item.remainingStock,
});
});
},
});
}
```
## Domain Creation Checklist
When creating a new domain:
### 1. Identify the Domain
* [ ] Clear business entity (e.g., products, users)
* [ ] Has distinct API endpoints
* [ ] Contains related types and operations
### 2. Create the Structure
* [ ] Create folder: `core/domains/[domain-name]/`
* [ ] Add required files: `api.ts`, `types.ts`, `hooks.ts`
* [ ] Add optional files as needed
* [ ] Create barrel export: `index.ts`
### 3. Implement Core Functionality
* [ ] Define types in `types.ts`
* [ ] Implement API methods in `api.ts`
* [ ] Create query/mutation hooks in `hooks.ts`
* [ ] Add query keys if > 3 queries
### 4. Handle Special Cases
* [ ] Add client state if needed (`store.ts`)
* [ ] Implement events if needed (`events.ts`)
* [ ] Add domain utilities if needed (`utils.ts`)
### 5. Update Documentation
* [ ] Add domain to architecture docs
* [ ] Document special behaviors
* [ ] Update team guidelines
## Best Practices
**Why These Practices Matter**
These patterns have evolved through our experience building and maintaining large-scale React Native applications. They help ensure consistency, maintainability, and scalability as your application grows and as team members come and go.
### 1. API Service Patterns
**(Do ✅) Use the public/protected pattern for all domain APIs**
This pattern creates a clear separation between endpoints that require authentication and those that don't, which helps prevent security mistakes and improves developer experience:
```typescript
class ProductApi {
public readonly public = {
// Endpoints that don't require authentication
getAll: async (): Promise => {
const { data } = await publicApi.get('/products');
return data;
},
};
public readonly protected = {
// Endpoints that require authentication
create: async (product: CreateProductDto): Promise => {
const { data } = await authenticatedApi.post('/products', product);
return data;
},
};
}
```
The benefits of this approach include:
* Clear visual distinction between authenticated and unauthenticated endpoints
* Compile-time safety through TypeScript's property access checking
* Better IntelliSense support with grouped methods
* No runtime checks or flags needed for auth requirements
**(Don't ❌) Use API clients directly or rely on configuration flags**
Avoid approaches that mix authentication concerns or rely on flags:
```typescript
// DON'T do this
export const productApi = {
// Unclear which endpoints need authentication
getAll: () => apiClient.get('/products', { skipAuth: true }),
create: (data) => apiClient.post('/products', data),
};
```
This approach creates several problems:
* Authentication requirements are not obvious from the code structure
* Easy to forget the `skipAuth` flag and cause runtime errors
* No compile-time safety for authentication requirements
* Harder to maintain consistency as the codebase grows
### 2. Domain Boundaries and Focus
**(Do ✅) Keep domains focused on a single business entity**
A well-designed domain should represent a single business concept with clear boundaries:
```typescript
// core/domains/products/api.ts
class ProductApi {
public readonly public = {
getAll: async (): Promise => {
const { data } = await publicApi.get('/products');
return data;
},
getById: async (id: string): Promise => {
const { data } = await publicApi.get(`/products/${id}`);
return data;
},
};
public readonly protected = {
create: async (product: CreateProductDto): Promise => {
const { data } = await authenticatedApi.post('/products', product);
return data;
},
update: async (id: string, updates: UpdateProductDto): Promise => {
const { data } = await authenticatedApi.put(`/products/${id}`, updates);
return data;
},
delete: async (id: string): Promise => {
await authenticatedApi.delete(`/products/${id}`);
},
};
}
```
By keeping domains focused, you gain several benefits:
* **Easier maintenance**: Each domain is smaller and more manageable
* **Better code organization**: Logical grouping of related functionality
* **Clearer ownership**: Teams can own specific domains
* **More targeted testing**: Domain-specific test suites
**(Don't ❌) Mix multiple business entities in a single domain**
Mixing responsibilities creates confusion and maintenance challenges:
```typescript
// DON'T do this
class ProductAndUserApi {
public readonly public = {
// Product methods
getProducts: async (): Promise => {
const { data } = await publicApi.get('/products');
return data;
},
getProductById: async (id: string): Promise => {
const { data } = await publicApi.get(`/products/${id}`);
return data;
},
// User methods - should be in users domain
getUsers: async (): Promise => {
const { data } = await publicApi.get('/users');
return data;
},
getUserById: async (id: string): Promise => {
const { data } = await publicApi.get(`/users/${id}`);
return data;
},
};
};
```
This approach creates several problems:
* **Unclear domain boundaries**: Where does one entity end and another begin?
* **Harder to maintain**: Changes to one entity might affect the other
* **Difficult testing**: Tests for different entities are mixed together
* **Poor code organization**: Code for different business concepts is intermingled
* **Challenges for team ownership**: Hard to assign ownership to different teams
### 3. Use Consistent Naming
Consistent naming patterns significantly improve developer experience by making code more predictable and easier to navigate. They also reduce the cognitive load when switching between different parts of the codebase.
**(Do ✅) Follow established naming conventions for all domain artifacts**
These conventions make code more predictable and discoverable:
| Type | Pattern | Examples |
| -------------- | ----------------------- | ----------------------------------------------------- |
| API instances | `[domain]Api` | `productApi`, `userApi`, `orderApi` |
| Query hooks | `use[Resource]` | `useProducts`, `useUser`, `useOrder` |
| Mutation hooks | `use[Action][Resource]` | `useCreateProduct`, `useUpdateUser`, `useDeleteOrder` |
| State stores | `use[Domain]Store` | `useProductStore`, `useCartStore` |
| Events | `[domain]Events` | `productEvents`, `authEvents` |
| Query keys | `[domain]QueryKeys` | `productQueryKeys`, `userQueryKeys` |
**(Consider 🤔) Creating a domain naming cheat sheet for your team**
As your application grows, consider documenting naming patterns in a quick-reference format for new team members.
**(Don't ❌) Mix naming conventions or create inconsistent patterns**
Inconsistent naming creates confusion and slows down development:
```typescript
// DON'T do this - inconsistent naming
// Different naming styles for similar concepts
export const productApiService = new ProductApiService(); // Too verbose
export const userApiClient = new UserApiClient(); // Different suffix
export const orderService = new OrderService(); // Missing "Api"
// Inconsistent hook naming
export function useGetProducts() { /* ... */ } // "Get" is redundant
export function fetchUsers() { /* ... */ } // Missing "use" prefix
export function orderQueryHook() { /* ... */ } // Unclear purpose
```
Consistent naming provides several benefits:
* **Predictability**: Developers can guess file and export names correctly
* **Clarity**: The purpose of each artifact is immediately clear
* **Maintainability**: Easier to implement tooling and linting rules
* **Onboarding**: New team members learn patterns more quickly
### 4. Separate Public and Protected Hooks
The authentication requirements of your hooks should match the authentication requirements of the underlying API methods they use. This creates a consistent security model throughout your application.
**(Do ✅) Match hook authentication requirements to API authentication requirements**
When creating hooks, ensure their authentication requirements align with the API methods they call:
```typescript
// Public hooks (no auth required) call public API methods
export function useProducts() {
return useQuery({
queryKey: productQueryKeys.list(),
queryFn: () => productApi.public.getAll(),
});
}
// Protected hooks (auth required) call protected API methods
export function useCreateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: productApi.protected.create,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: productQueryKeys.lists() });
},
});
}
```
This alignment provides several benefits:
* **Security consistency**: Authentication requirements are clear at every level
* **Predictable behavior**: Hooks fail in expected ways when auth is missing
* **Better developer experience**: API structure guides hook organization
**(Don't ❌) Mix authentication requirements or add unnecessary authentication**
These approaches create confusion and security risks:
```typescript
// DON'T do this
// Public data shouldn't require authentication
export function usePublicProducts() {
// Wrong: Using protected API for public data
return useQuery({
queryKey: ['products'],
queryFn: () => protectedApiClient.get('/public-products'),
});
}
// Don't add conditional authentication
export function useProducts(requireAuth = false) {
// Wrong: Dynamic authentication creates unpredictable behavior
const client = requireAuth ? protectedApiClient : publicApiClient;
return useQuery({
queryKey: ['products', { auth: requireAuth }],
queryFn: () => client.get('/products'),
});
}
```
**(Consider 🤔) Adding explicit documentation about authentication requirements**
For larger teams, consider adding JSDoc comments that explicitly state authentication requirements:
```typescript
/**
* Hook to fetch products list - no authentication required
*/
export function useProducts() { /* ... */ }
/**
* Hook to create a new product - requires authentication
* @throws {UnauthorizedError} When user is not authenticated
*/
export function useCreateProduct() { /* ... */ }
```
### 5. Handle Errors Gracefully
Error handling is a critical aspect of domain hooks. Well-designed error handling improves the user experience and makes debugging easier.
**(Do ✅) Implement consistent error handling in all query hooks**
```typescript
export function useProduct(id: string) {
return useQuery({
queryKey: productQueryKeys.detail(id),
queryFn: async () => {
try {
return await productApi.public.getById(id);
} catch (error) {
// Transform API errors to more user-friendly format
if (error.status === 404) {
throw new ProductNotFoundError(id);
}
throw error; // Re-throw other errors
}
},
enabled: !!id,
retry: (failureCount, error) => {
// Only retry for network errors, not for 404s
if (error instanceof ProductNotFoundError) return false;
return failureCount < 3;
},
});
}
```
### 6. Optimize Query Patterns
```typescript
// Use query options effectively
export function useProducts(options?: UseQueryOptions) {
return useQuery({
queryKey: productQueryKeys.all(),
queryFn: productApi.public.getAll,
staleTime: 5 * 60 * 1000, // 5 minutes
...options,
});
}
```
## Anti-Patterns to Avoid
### 1. Direct State Mutation
❌ **Wrong**: Mutating cache directly
```typescript
const queryClient = useQueryClient();
const products = queryClient.getQueryData(['products']);
products.push(newProduct); // Don't do this!
```
✅ **Correct**: Use proper mutations
```typescript
const createProduct = useCreateProduct();
createProduct.mutate(newProduct);
```
### 2. Circular Dependencies
❌ **Wrong**: Domains importing each other
```typescript
// core/domains/products/api.ts
import { userApi } from '@/core/domains/users/api'; // Avoid!
```
✅ **Correct**: Use shared hooks or events
```typescript
// In feature component
const { data: product } = useProduct(id);
const { data: user } = useUser(product?.ownerId);
```
### 3. Business Logic in Features
❌ **Wrong**: Calculate in features
```typescript
// features/product-catalog/components/ProductCard.tsx
const discountPrice = product.price * 0.9; // Business logic!
```
✅ **Correct**: Domain handles logic
```typescript
// core/domains/products/utils.ts
export function calculateDiscount(product: Product): number {
return product.price * 0.9;
}
```
## Related Documents
* [Core Architecture Reference](/docs/architecture/core-architecture)
* [Project Structure Reference](/docs/architecture/project-structure)
* [Shared Utilities Guide](/docs/features/shared-utilities)
* [File Naming Conventions](/docs/coding-standards/file-naming-conventions)
* [Communication Patterns](/docs/features/communication-patterns)
* [API Integration Guide](/docs/api)
* [State Management Guide](/docs/guides/state-management)
# UTA Architecture: Architecture Decision Record Template
URL: https://dev.updatetheapp.com/docs/architecture/adr-template
Documentation: https://dev.updatetheapp.com/docs/architecture/adr-template
Template for documenting architectural decisions with emphasis on explaining rationale and context
***
title: Architecture Decision Record Template
description: Template for documenting architectural decisions with emphasis on explaining rationale and context
---------------------------------------------------------------------------------------------------------------
import { Callout } from 'fumadocs-ui/components/callout';
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
# Architecture Decision Record (ADR) Template
## Overview
Architecture Decision Records (ADRs) help our team document important architectural choices, their context, and most importantly, the reasoning behind them. This creates a valuable knowledge base that helps current and future team members understand not just *what* we decided, but *why* we made those choices.
**Why ADRs Matter**: ADRs prevent "archaeology" - the need to dig through old code and commits to understand why things are the way they are. They capture the context and constraints that influenced our decisions, which often gets lost over time.
## ADR Structure
### ADR-\[NUMBER]: \[Descriptive Title]
**Date**: \[YYYY-MM-DD]\
**Status**: \[Proposed | Accepted | Deprecated | Superseded]\
**Deciders**: \[List of people involved in the decision]\
**Technical Story**: \[Ticket/Issue reference if applicable]
### Context and Problem Statement
\[Describe the situation that necessitates this decision. What challenge are we facing? What opportunity are we addressing? Write this in 2-3 clear sentences that someone new to the project could understand.]
### Why is this decision necessary?
\[Explain the importance and urgency of making this decision. What happens if we don't address this? What pain points does it solve? This section helps readers understand the motivation.]
### Decision Drivers
Key factors influencing our decision:
* \[Driver 1 - e.g., Performance requirements]
* \[Driver 2 - e.g., Team expertise]
* \[Driver 3 - e.g., Time constraints]
* \[Additional drivers as needed]
### Considered Options
We evaluated the following approaches:
1. **\[Option 1 Name]** - \[Brief description]
2. **\[Option 2 Name]** - \[Brief description]
3. **\[Option 3 Name]** - \[Brief description]
### Decision Outcome
**Chosen option**: "\[Selected Option]"
We selected this option because \[provide clear justification linking back to the decision drivers].
#### Why this option?
\[Provide detailed explanation of why this option best addresses our needs. Include:
* How it satisfies our key requirements
* What trade-offs we're accepting
* Why other options were less suitable]
### Consequences
#### Positive Outcomes
* \[Benefit 1 - e.g., Improved developer experience]
* \[Benefit 2 - e.g., Better performance]
* \[Additional benefits]
#### Trade-offs and Considerations
* \[Trade-off 1 - e.g., Increased complexity in certain areas]
* \[Trade-off 2 - e.g., Additional learning curve]
* \[Other considerations]
### Implementation Details
#### File Structure Impact
```
# Show how this decision affects project organization
src/
├── core/
│ └── [new/modified structure]
└── features/
└── [affected areas]
```
#### Code Examples
```typescript
// Current implementation approach
[Show existing pattern or structure]
```
```typescript
// New implementation following this decision
[Show new pattern with helpful comments]
```
### Validation
How we'll measure success:
* \[Metric 1 - e.g., 50% reduction in related bugs]
* \[Metric 2 - e.g., New developer onboarding time under 2 days]
* \[Metric 3 - e.g., Decreased "where does this go?" questions]
### Related Resources
* \[Link to related ADRs]
* \[Link to implementation documentation]
* \[Link to example implementations]
### ADR-001: Three-Layer UI Architecture
**Date**: 2024-01-15\
**Status**: Accepted\
**Deciders**: Tech Lead, Senior UI Engineers, Frontend Team\
**Technical Story**: JIRA-1234
### Context and Problem Statement
Our UI components have grown organically without clear categorization guidelines. Developers spend significant time deciding where to place new components, and we're seeing duplication because existing components are hard to discover. We need a clear, scalable approach to organizing our UI layer.
### Why is this decision necessary?
Without clear component organization:
* Developers waste 15-30 minutes per component deciding placement
* We've found 23 duplicate button variations across features
* Business logic is leaking into what should be presentational components
* Component reusability is suffering, increasing bundle size
If we don't address this now, these issues will compound as the team grows.
### Decision Drivers
Key factors influencing our decision:
* **Clarity**: Need unambiguous rules for component placement
* **Reusability**: Must maximize component sharing across features
* **Separation of Concerns**: Keep business logic separate from presentation
* **Team Scalability**: System must work for both junior and senior developers
* **Migration Path**: Solution must allow incremental adoption
### Considered Options
We evaluated four architectural approaches:
1. **Three-layer architecture** (foundation/patterns/business)
* Clear separation between atomic, composed, and domain-aware components
2. **Two-layer architecture** (components/containers)
* Simple split between presentational and container components
3. **Feature-first organization** (all UI in features)
* Co-locate all UI with its feature code
4. **Atomic design** (atoms/molecules/organisms/templates/pages)
* Industry-standard five-level hierarchy
### Decision Outcome
**Chosen option**: "Three-layer architecture"
We selected this approach because it provides the optimal balance of clarity and flexibility while addressing all our key requirements.
#### Why this option?
The three-layer architecture best serves our needs because:
* **Foundation layer** (atoms) enforces zero business logic in basic components, ensuring maximum reusability
* **Patterns layer** (molecules) allows sophisticated UI composition without domain coupling
* **Business layer** (organisms) provides a clear home for domain-aware components shared across features
This approach is superior to alternatives because:
* Clearer than atomic design's 5 levels (which often confuse developers)
* More flexible than the rigid container/component split
* Better for reusability than feature-first organization
* Maps naturally to how our team already thinks about components
### Consequences
#### Positive Outcomes
* Clear, memorable rules reduce decision time to under 2 minutes
* Improved component discovery through logical organization
* Better code reuse (estimated 40% reduction in duplicate components)
* Easier onboarding - new developers understand the system in hours, not days
* Natural enforcement of separation of concerns
#### Trade-offs and Considerations
* Initial migration effort (estimated 2 sprints for full adoption)
* Some components may sit at layer boundaries (we'll document edge cases)
* Team training required (planned lunch-and-learn sessions)
* Need to update build tools for new structure
### Implementation Details
#### File Structure Impact
```
src/ui/
├── foundation/ # Atomic components (no business logic)
│ ├── Button/
│ ├── Input/
│ ├── Card/
│ └── Icon/
├── patterns/ # Composed UI patterns (domain-agnostic)
│ ├── Modal/
│ ├── Form/
│ ├── DataTable/
│ └── SearchBox/
└── business/ # Domain-aware components (3+ features)
├── ProductCard/
├── UserAvatar/
└── OrderStatus/
```
#### Code Examples
```typescript
// ui/foundation/Button/Button.tsx
import { theme } from '@/core/shared/styles/theme';
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'small' | 'medium' | 'large';
onPress: () => void;
children: React.ReactNode;
}
// Pure presentational component - no business logic
export function Button({ variant = 'primary', size = 'medium', onPress, children }: ButtonProps) {
return (
{children}
);
}
```
```typescript
// ui/patterns/Modal/Modal.tsx
import { Button } from '@/ui/foundation/Button';
import { Card } from '@/ui/foundation/Card';
interface ModalProps {
visible: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}
// Composed from foundation components, handles UI behavior only
export function Modal({ visible, onClose, title, children }: ModalProps) {
if (!visible) return null;
return (
{title}
{children}
);
}
```
```typescript
// ui/business/ProductCard/ProductCard.tsx
import { Card } from '@/ui/foundation/Card';
import { Button } from '@/ui/foundation/Button';
import { PriceTag } from '@/ui/patterns/PriceTag';
import { useProduct } from '@/core/domains/products/hooks';
import { useCart } from '@/core/domains/cart/hooks';
interface ProductCardProps {
productId: string;
}
// Domain-aware component that uses business logic
export function ProductCard({ productId }: ProductCardProps) {
const { data: product, isLoading } = useProduct(productId);
const { addToCart } = useCart();
if (isLoading) return ;
if (!product) return null;
return (
{product.name}
);
}
```
### Validation
We'll measure success through:
* **Placement Decision Time**: Target \< 2 minutes (from current 15-30 min)
* **Component Duplication**: 40% reduction within 3 months
* **Developer Satisfaction**: Survey scores > 4/5 on component organization
* **Onboarding Efficiency**: New developers productive within 1 week
* **Code Review Mentions**: 80% reduction in component placement discussions
### Related Resources
* [UI Architecture Guide](/docs/architecture/ui-architecture) - Detailed implementation guide
* [Component Development Guide](/docs/guides/component-development) - Best practices
* [Migration Guide](/docs/guides/ui-migration) - Step-by-step migration plan
### Writing Effective ADRs
**Remember**: ADRs are written for future readers, including yourself in 6 months. Be kind to your future self!
#### Key Principles
1. **Be Concise but Complete**
* Include enough context for understanding
* Don't write a novel - aim for 2-3 pages max
* Focus on the "why" more than the "how"
2. **Consider Your Audience**
* Write for developers who might join the team later
* Avoid assuming deep knowledge of current context
* Define project-specific terms
3. **Make it Discoverable**
* Use descriptive titles
* Include relevant keywords
* Link from related documentation
#### Common Pitfalls to Avoid
* **Too Technical**: Balance technical accuracy with readability
* **Missing Context**: Always explain the problem before the solution
* **No Alternatives**: Show that you considered multiple options
* **Vague Consequences**: Be specific about trade-offs
#### When to Write an ADR
Create an ADR when:
* Choosing between architectural patterns
* Adopting new technologies or libraries
* Making changes that affect multiple teams/features
* Decisions that are expensive to reverse
* Team debates indicate lack of clarity
**Pro Tip**: If you find yourself explaining the same decision multiple times, it probably needs an ADR!
## Related Documents
* [Project Structure](/docs/architecture/project-structure) - Understanding our codebase organization
* [Domain Architecture](/docs/architecture/domain-architecture) - How we structure business logic
# UTA State Management: State Management Architecture
URL: https://dev.updatetheapp.com/docs/state-management/state-management-architecture
Documentation: https://dev.updatetheapp.com/docs/state-management/state-management-architecture
Overview of our state management approach and the golden rule for separating server and client state
***
title: State Management Architecture
description: Overview of our state management approach and the golden rule for separating server and client state
-----------------------------------------------------------------------------------------------------------------
import { File, Folder, Files } from 'fumadocs-ui/components/files';
# State Management Architecture
## Overview
This document outlines our application's state management architecture, built around the **Golden Rule** - our fundamental principle for state separation. This architecture optimizes for performance, maintainability, and developer experience by ensuring each type of state (server, client, and component) is handled by the tool best suited for its characteristics. By following a clear separation of concerns, we reduce complexity and improve both runtime performance and developer productivity.
## Purpose & Scope
* **Target Audience**: React Native developers working on this application who need to understand when and how to use different state management tools
* **Problems Addressed**: State management complexity, prop drilling, unnecessary re-renders, and data synchronization challenges
* **Scope Boundaries**: Covers the three primary state management patterns and their integration, but does not cover persistence strategies or offline capabilities in depth
## The Golden Rule
> **Server state belongs in TanStack Query, Client state belongs in Zustand, Component state belongs in React**
This foundational principle guides all state management decisions in our application:
Remote Data]
ClientState[Client State UI State]
ComponentState[Component State Local State]
end
subgraph "Management Tools"
TanStack[TanStack Query]
Zustand[Zustand]
React[React useState/useReducer]
end
ServerState --> TanStack
ClientState --> Zustand
ComponentState --> React
style ServerState fill:#bbf,stroke:#333,stroke-width:2px
style ClientState fill:#ff9,stroke:#333,stroke-width:2px
style ComponentState fill:#f9f,stroke:#333,stroke-width:2px
style TanStack fill:#bbf,stroke:#333,stroke-width:2px
style Zustand fill:#ff9,stroke:#333,stroke-width:2px
style React fill:#f9f,stroke:#333,stroke-width:2px
`}
/>
### When to Apply the Golden Rule
* **(Do ✅)** Use TanStack Query when the data comes from an API or remote source
* **(Do ✅)** Use Zustand when state needs to be shared across components or persisted
* **(Do ✅)** Use React state when the state is only relevant to a single component
## Core Components/Architecture
### Component Types
| Component | Responsibility | Implementation |
| --------------- | ---------------------------- | ------------------------- |
| Server State | Remote data from APIs | TanStack Query |
| Client State | Application-wide UI state | Zustand |
| Component State | Local component interactions | React useState/useReducer |
### When to Apply Each Component
* **(Do ✅)** Use TanStack Query when the data comes from an API
* **(Do ✅)** Use Zustand when state needs to be shared across components or persisted
* **(Do ✅)** Use React state when the state is only relevant to a single component
### Communication Patterns
Query[TanStack Query Hooks]
Component --> Store[Zustand Store]
Component --> Local[React useState]
Query --> QueryClient[Query Client]
QueryClient --> API[API Layer]
API --> Backend[Backend Server]
Store --> PersistLayer[Optional Persist Layer]
PersistLayer --> Storage[AsyncStorage]
style Component fill:#f5f5f5,stroke:#333,stroke-width:2px
style Query fill:#bbf,stroke:#333,stroke-width:2px
style Store fill:#ff9,stroke:#333,stroke-width:2px
style Local fill:#f9f,stroke:#333,stroke-width:2px
`}
/>
## Design Principles
### Core Architectural Principles
* **(Do ✅)** Maintain a clear separation of concerns between server and client state
* Server state and client state have fundamentally different characteristics and should be treated differently
* Mixing concerns leads to unnecessary complexity and performance issues
* **(Do ✅)** Use specialized tools optimized for each state type
* TanStack Query handles caching, invalidation, and refetching
* Zustand provides a simple API for global state with minimal boilerplate
* React state is perfect for component-level concerns
* **(Do ✅)** Keep components pure and focused on rendering
* Components should primarily transform props into UI
* Delegate state management logic to hooks and stores
### Trade-offs and Design Decisions
| Decision | Benefits | Trade-offs | Why We Chose It |
| ------------------------------- | ------------------------------------------------- | --------------------------- | --------------------------- |
| Separate server & client state | Better performance, clearer code | Learning multiple tools | Benefits outweigh costs |
| TanStack Query for server state | Automatic caching, refetching, optimistic updates | More code than simple fetch | Drastically improves UX |
| Zustand for global state | Simple API, minimal boilerplate | Another dependency | Less complex than Redux |
| React state for component state | Built-in, familiar | No persistence | Perfect for ephemeral state |
## Implementation Considerations
### Performance Implications
* **Query Caching**: Server state is automatically cached by TanStack Query, reducing unnecessary network requests
* **Zustand Selectors**: Use selectors to prevent unnecessary re-renders by only subscribing to relevant slices of state
* **Component Memoization**: Leverage React.memo and useMemo to prevent wasteful re-renders
### Scaling Considerations
* **Query Client Configuration**: Set up appropriate staleTime and cacheTime based on data volatility
* **Store Slicing**: Break down Zustand stores by domain as the application grows
* **Persistence Strategy**: Configure storage persistence only for critical state to avoid bloat
## Related Documents
* [Server State Management](/docs/state-management/server-state) - Detailed guide on TanStack Query implementation
* [Client State Management](/docs/state-management/client-state) - Zustand patterns and best practices
* [Component State](/docs/state-management/component-state) - React state management techniques
* [State Integration Patterns](/docs/state-management/integration-patterns) - How different state types work together
# UTA State Management: Server State Management
URL: https://dev.updatetheapp.com/docs/state-management/server-state
Documentation: https://dev.updatetheapp.com/docs/state-management/server-state
Comprehensive guide for managing server state with TanStack Query in our React Native application
***
title: Server State Management
description: Comprehensive guide for managing server state with TanStack Query in our React Native application
--------------------------------------------------------------------------------------------------------------
import { File, Folder, Files } from 'fumadocs-ui/components/files';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
# Server State Management
## Overview
This document outlines our approach to managing server state - data that originates from external APIs and requires caching, synchronization, and complex loading states. Our architecture implements TanStack Query (React Query) as the dedicated solution for all server state management, following our application's **Golden Rule**: "Server state belongs in TanStack Query, not in Zustand or Redux". This approach optimizes for performance, developer experience, and data consistency across the application.
## Purpose & Scope
* **Target Audience**: React Native developers implementing features that require API data fetching, caching, or state synchronization
* **Problems Addressed**: API data management, caching strategies, loading/error states, and data synchronization challenges
* **Scope Boundaries**: Covers TanStack Query implementation, query patterns, mutation strategies, and caching approaches but does not cover API service implementation details or authentication token management
## Core Components/Architecture
QueryHook[Query Hook]
Component --> MutationHook[Mutation Hook]
QueryHook --> QueryClient[Query Client]
MutationHook --> QueryClient
QueryClient --> Cache[(Query Cache)]
QueryClient --> API[API Layer]
API --> Backend[Backend Server]
subgraph "Server State Layer"
QueryHook
MutationHook
QueryClient
Cache
end
style QueryHook fill:#bbf,stroke:#333,stroke-width:2px
style MutationHook fill:#bbf,stroke:#333,stroke-width:2px
style QueryClient fill:#bbf,stroke:#333,stroke-width:2px
style Cache fill:#bbf,stroke:#333,stroke-width:2px
`}
/>
### Component Types
| Component | Responsibility | Implementation |
| -------------- | ------------------------------------- | ----------------------------- |
| Query Hooks | Data fetching and caching | Feature-specific custom hooks |
| Mutation Hooks | Data updating and optimistic updates | Feature-specific custom hooks |
| Query Client | Central management and configuration | Globally configured instance |
| Query Cache | In-memory data storage | Managed by Query Client |
| Query Keys | Cache identification and organization | Structured array patterns |
### When to Use Server State
TanStack Query should be used for any data that meets these criteria:
* **(Do ✅)** Use for data fetched from external APIs or backend services
* **(Do ✅)** Use for data that requires caching and deduplication
* **(Do ✅)** Use for data with complex loading or error states
* **(Do ✅)** Use for data that needs synchronization across components
* **(Do ✅)** Use for real-time data that requires polling or refetching
## Design Principles
### Core Architectural Principles
* **(Do ✅)** Keep server state separate from client state
* Server state should be managed by TanStack Query
* Client state should be managed by Zustand
* Mixing these concerns leads to unnecessary complexity
* **(Do ✅)** Create focused, reusable query hooks
* Each query hook should serve a specific data need
* Hooks should be feature-specific and located in the appropriate feature directory
* Abstract complex query logic into custom hooks
* **(Don't ❌)** Store server data in Zustand or React context
* Avoid creating duplicate copies of server state
* Let TanStack Query handle caching and synchronization
### Data Access Patterns
| Pattern | Used For | Implementation |
| ------------------ | ---------------------------------- | ------------------------------------------ |
| Basic Queries | Simple data fetching | `useQuery` with query keys |
| Dependent Queries | Data that depends on other queries | `useQuery` with `enabled` option |
| Paginated Queries | Lists with pagination | `useQuery` with pagination params |
| Infinite Queries | Endless scrolling lists | `useInfiniteQuery` with `getNextPageParam` |
| Mutations | Creating, updating, deleting data | `useMutation` with callbacks |
| Optimistic Updates | Immediate UI feedback | `useMutation` with `onMutate` |
### Query Key Strategies
Entity["Entity Type"]
Entity --> Collection["Collection/Detail"]
Collection --> Params["Parameters"]
style Root fill:#bbf,stroke:#333,stroke-width:2px
style Entity fill:#bbf,stroke:#333,stroke-width:2px
style Collection fill:#bbf,stroke:#333,stroke-width:2px
style Params fill:#bbf,stroke:#333,stroke-width:2px
`}
/>
Query keys should follow a consistent structure:
```typescript
// Pattern for detail queries
['entity', 'detail', entityId, ...params]
// Pattern for list queries
['entity', 'list', { filters }]
```
### Trade-offs and Design Decisions
**Centralized vs. Distributed Query Hooks**
| Approach | Benefits | Drawbacks | Best For |
| --------------------- | ------------------------------------------- | ----------------------------------------- | -------------------- |
| Centralized API hooks | Consistency, single source of truth | Potential for large files, tight coupling | Small to medium apps |
| Feature-based hooks | Better code organization, feature isolation | Potential duplication | Medium to large apps |
**Our Decision**: We use a feature-based organization for query hooks while sharing common functionality through a factory pattern, giving us both organization and reusability.
## Implementation Considerations
### Performance Implications
* **(Do ✅)** Configure appropriate staleTime based on data volatility
* More static data (e.g., product details): Longer staleTime (5-10 minutes)
* More dynamic data (e.g., cart): Shorter staleTime (0-30 seconds)
* **(Do ✅)** Use the `select` option to transform and minimize data
* Transform and filter data on selection to minimize re-renders
* Extract only the needed fields from larger response objects
* **(Don't ❌)** Fetch the same data in multiple components
* Create reusable query hooks to share data between components
* Let TanStack Query handle the caching and deduplication
### Security Considerations
* **(Do ✅)** Validate all data from server responses
* Use TypeScript interfaces to enforce data shape
* Consider runtime validation with libraries like Zod or Yup
* **(Do ✅)** Properly handle authentication errors
* Set up global error handling for 401 responses
* Redirect to login page or refresh tokens as needed
* **(Don't ❌)** Store sensitive data in the query cache without encryption
* Be cautious when persisting the query cache to storage
* Consider what data should be excluded from persistence
### Scalability Aspects
* **(Do ✅)** Implement structured query keys for efficient invalidation
* Create a query key factory for consistent key structure
* Use parent keys to invalidate related queries
* **(Consider 🤔)** Setting up query cache persistence
* Improves offline experience and reduces initial loading
* Balance with cache size management and potential staleness
* **(Be Aware ❗)** Of memory usage with large datasets
* Implement pagination or infinite queries for large lists
* Consider data normalization for frequently used entities
## Implementation Examples
### Setting Up TanStack Query
```typescript
// core/query/queryClient.ts
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
refetchOnWindowFocus: false,
refetchOnMount: true,
},
mutations: {
retry: 1,
},
},
});
// App.tsx
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '@/core/query';
export function App() {
return (
{/* Your app components */}
);
}
```
### Query Key Factory
```typescript
// core/query/queryKeys.ts
export const queryKeys = {
all: ['queries'] as const,
users: {
all: ['users'] as const,
lists: () => [...queryKeys.users.all, 'list'] as const,
list: (filters: string) => [...queryKeys.users.lists(), { filters }] as const,
details: () => [...queryKeys.users.all, 'detail'] as const,
detail: (id: string) => [...queryKeys.users.details(), id] as const,
},
products: {
all: ['products'] as const,
lists: () => [...queryKeys.products.all, 'list'] as const,
list: (filters: any) => [...queryKeys.products.lists(), { filters }] as const,
details: () => [...queryKeys.products.all, 'detail'] as const,
detail: (id: string) => [...queryKeys.products.details(), id] as const,
},
cart: {
all: ['cart'] as const,
current: () => [...queryKeys.cart.all, 'current'] as const,
},
};
```
### Query Patterns
```typescript
// features/products/api/productHooks.ts
import { useQuery } from '@tanstack/react-query';
import { productService } from '@/core/api/services';
import { queryKeys } from '@/core/query';
export const useProductsQuery = (filters = {}) => {
return useQuery({
queryKey: queryKeys.products.list(filters),
queryFn: () => productService.getProducts(filters),
staleTime: 5 * 60 * 1000, // 5 minutes
});
};
export const useProductQuery = (productId: string) => {
return useQuery({
queryKey: queryKeys.products.detail(productId),
queryFn: () => productService.getProduct(productId),
enabled: !!productId,
});
};
```
```typescript
// features/users/api/userHooks.ts
interface UserResponse {
id: string;
first_name: string;
last_name: string;
email_address: string;
}
interface User {
id: string;
name: string;
email: string;
}
export const useUserQuery = (userId: string) => {
return useQuery({
queryKey: queryKeys.users.detail(userId),
queryFn: () => userService.getUser(userId),
select: (data: UserResponse): User => ({
id: data.id,
name: `${data.first_name} ${data.last_name}`,
email: data.email_address,
}),
});
};
```
```typescript
// features/orders/api/orderHooks.ts
export const useOrderWithDetailsQuery = (orderId: string) => {
const orderQuery = useQuery({
queryKey: queryKeys.orders.detail(orderId),
queryFn: () => orderService.getOrder(orderId),
});
const orderItemsQuery = useQuery({
queryKey: queryKeys.orders.items(orderId),
queryFn: () => orderService.getOrderItems(orderId),
enabled: !!orderQuery.data,
});
return {
order: orderQuery.data,
items: orderItemsQuery.data,
isLoading: orderQuery.isLoading || orderItemsQuery.isLoading,
error: orderQuery.error || orderItemsQuery.error,
};
};
```
### Mutation Patterns
```typescript
// features/products/api/productMutations.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { productService } from '@/core/api/services';
import { queryKeys } from '@/core/query';
export const useCreateProductMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: productService.createProduct,
onSuccess: (newProduct) => {
// Invalidate and refetch products list
queryClient.invalidateQueries({ queryKey: queryKeys.products.lists() });
// Add the new product to the cache
queryClient.setQueryData(
queryKeys.products.detail(newProduct.id),
newProduct
);
},
});
};
```
```typescript
// features/cart/api/cartMutations.ts
export const useUpdateCartItemMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: cartService.updateCartItem,
onMutate: async ({ itemId, quantity }) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({ queryKey: queryKeys.cart.current() });
// Snapshot the previous value
const previousCart = queryClient.getQueryData(queryKeys.cart.current());
// Optimistically update
queryClient.setQueryData(queryKeys.cart.current(), (old: Cart) => ({
...old,
items: old.items.map(item =>
item.id === itemId ? { ...item, quantity } : item
),
}));
// Return a context with the previous value
return { previousCart };
},
onError: (err, variables, context) => {
// If the mutation fails, use the context to roll back
if (context?.previousCart) {
queryClient.setQueryData(queryKeys.cart.current(), context.previousCart);
}
},
onSettled: () => {
// Always refetch after error or success
queryClient.invalidateQueries({ queryKey: queryKeys.cart.current() });
},
});
};
```
```typescript
// features/checkout/api/checkoutMutations.ts
export const useCheckoutMutation = () => {
const queryClient = useQueryClient();
const createOrderMutation = useMutation({
mutationFn: orderService.createOrder,
});
const processPaymentMutation = useMutation({
mutationFn: paymentService.processPayment,
});
return useMutation({
mutationFn: async (checkoutData: CheckoutData) => {
// Create order first
const order = await createOrderMutation.mutateAsync({
items: checkoutData.items,
shippingAddress: checkoutData.shippingAddress,
});
// Then process payment
const payment = await processPaymentMutation.mutateAsync({
orderId: order.id,
paymentMethod: checkoutData.paymentMethod,
amount: order.total,
});
return { order, payment };
},
onSuccess: () => {
// Clear cart and invalidate related queries
queryClient.invalidateQueries({ queryKey: queryKeys.cart.all });
queryClient.invalidateQueries({ queryKey: queryKeys.orders.all });
},
});
};
```
### Advanced Patterns
```typescript
// features/feed/api/feedHooks.ts
import { useInfiniteQuery } from '@tanstack/react-query';
export const useFeedQuery = () => {
return useInfiniteQuery({
queryKey: queryKeys.feed.all,
queryFn: ({ pageParam = 1 }) => feedService.getFeedItems({ page: pageParam }),
getNextPageParam: (lastPage, pages) => {
return lastPage.hasMore ? lastPage.nextPage : undefined;
},
initialPageParam: 1,
});
};
// Component usage
const FeedList = () => {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useFeedQuery();
const items = data?.pages.flatMap(page => page.items) ?? [];
return (
}
onEndReached={() => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}}
ListFooterComponent={() =>
isFetchingNextPage ? : null
}
/>
);
};
```
```typescript
// features/notifications/api/notificationHooks.ts
export const useNotificationsQuery = () => {
return useQuery({
queryKey: queryKeys.notifications.unread,
queryFn: notificationService.getUnreadNotifications,
refetchInterval: 30 * 1000, // Poll every 30 seconds
refetchIntervalInBackground: true,
});
};
// App state change refetching
import { useEffect } from 'react';
import { AppState, AppStateStatus } from 'react-native';
import { useQueryClient } from '@tanstack/react-query';
export const useAppStateRefetch = () => {
const queryClient = useQueryClient();
useEffect(() => {
const handleAppStateChange = (nextAppState: AppStateStatus) => {
if (nextAppState === 'active') {
// Refetch all queries when app comes to foreground
queryClient.invalidateQueries();
}
};
const subscription = AppState.addEventListener('change', handleAppStateChange);
return () => subscription.remove();
}, [queryClient]);
};
```
```typescript
// features/products/screens/ProductListScreen.tsx
export const ProductListScreen = () => {
const queryClient = useQueryClient();
const { data: products } = useProductsQuery();
const handleProductPress = async (productId: string) => {
// Prefetch product details before navigation
await queryClient.prefetchQuery({
queryKey: queryKeys.products.detail(productId),
queryFn: () => productService.getProduct(productId),
});
navigation.navigate('ProductDetail', { productId });
};
return (
(
handleProductPress(item.id)}
/>
)}
/>
);
};
```
### Error Handling Strategies
```typescript
// core/query/queryClient.ts
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000,
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
onError: (error) => {
// Log the error
console.error('Query error:', error);
// You could trigger a global notification here
},
},
mutations: {
retry: 1,
onError: (error, variables, context) => {
// Log mutation errors
console.error('Mutation error:', error, variables);
// Show a toast message
Toast.show({
type: 'error',
text1: 'Operation failed',
text2: getErrorMessage(error),
});
},
},
},
});
```
````typescript
// core/hooks/useErrorHandler.ts
import { useCallback } from 'react';
import { Toast } from 'react-native-toast-message';
import { isAxiosError } from 'axios';
export const useErrorHandler = () => {
const handleError = useCallback((error: unknown, fallbackMessage = 'An error occurred') => {
let message = fallbackMessage;
if (isAxiosError(error)) {
if (error.response) {
// Server responded with an error status
const serverMessage = error.response.data?.message;
message = serverMessage || `Error ${error.response.status}: ${error.response.statusText}`;
// Handle specific status codes
if (error.response.status === 401) {
// Trigger auth refresh or logout workflow
// ...
return;
}
} else if (error.request) {
// Request was made but no response
message = 'No response from server. Please check your connection.';
}
} else if (error instanceof Error) {
message = error.message;
}
## Caching Strategies
```typescript
// core/query/queryClient.ts
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
// How long until the data is considered stale
staleTime: 5 * 60 * 1000, // 5 minutes
// How long to keep data in cache while unused
cacheTime: 10 * 60 * 1000, // 10 minutes
// Whether to refetch when window regains focus
refetchOnWindowFocus: true,
// Whether to refetch when component remounts
refetchOnMount: true,
// Whether to refetch when reconnecting
refetchOnReconnect: true,
},
},
});
````
Default settings establish baseline behavior for all queries. Adjust these settings based on app needs:
* **Short-lived data**: Low staleTime (30s)
* **Relatively stable data**: Medium staleTime (5-10min)
* **Rarely changing data**: Higher staleTime (30min-1hr)
```typescript
// features/products/api/productHooks.ts
// Frequently changing data - short staleTime
export const useProductInventoryQuery = (productId: string) => {
return useQuery({
queryKey: queryKeys.products.inventory(productId),
queryFn: () => productService.getInventory(productId),
staleTime: 30 * 1000, // 30 seconds
refetchInterval: 60 * 1000, // Poll every minute
});
};
// Stable reference data - long staleTime
export const useProductCategoriesQuery = () => {
return useQuery({
queryKey: queryKeys.products.categories(),
queryFn: productService.getCategories,
staleTime: 24 * 60 * 60 * 1000, // 24 hours
cacheTime: 7 * 24 * 60 * 60 * 1000, // 7 days
});
};
// Critical user data - always refetch on mount
export const useUserProfileQuery = (userId: string) => {
return useQuery({
queryKey: queryKeys.users.detail(userId),
queryFn: () => userService.getUser(userId),
staleTime: 0, // Always considered stale
refetchOnMount: 'always',
});
};
```
## Advanced Usage Patterns
```typescript
// features/products/api/productMutations.ts
export const useUpdateProductMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: productService.updateProduct,
onSuccess: (updatedProduct) => {
// Update specific product in cache
queryClient.setQueryData(
queryKeys.products.detail(updatedProduct.id),
updatedProduct
);
// Update product in any lists that might contain it
queryClient.setQueriesData(
{ queryKey: queryKeys.products.lists() },
(old: { products: Product[] }) => {
if (!old) return old;
return {
...old,
products: old.products.map(product =>
product.id === updatedProduct.id ? updatedProduct : product
),
};
}
);
},
});
};
```
```typescript
// features/cart/api/cartMutations.ts
export const useRemoveCartItemMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: cartService.removeItem,
onMutate: async (itemId) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({
queryKey: queryKeys.cart.current(),
});
// Snapshot the current cart
const previousCart = queryClient.getQueryData(queryKeys.cart.current());
// Optimistically update the cart
queryClient.setQueryData(queryKeys.cart.current(), (old) => ({
...old,
items: old.items.filter(item => item.id !== itemId),
totalItems: old.totalItems - 1,
totalPrice: old.totalPrice - (old.items.find(i => i.id === itemId)?.price || 0),
}));
return { previousCart };
},
onError: (_, __, context) => {
// Rollback on error
if (context?.previousCart) {
queryClient.setQueryData(queryKeys.cart.current(), context.previousCart);
}
},
onSettled: () => {
// Refetch to ensure data consistency
queryClient.invalidateQueries({ queryKey: queryKeys.cart.current() });
},
});
};
```
```typescript
// features/navigation/navigationHelpers.ts
import { queryClient } from '@/core/query';
import { queryKeys } from '@/core/query';
import { productService } from '@/core/api/services';
export const navigateToProductDetail = async (navigation, productId) => {
// Prefetch product details before navigation
await queryClient.prefetchQuery({
queryKey: queryKeys.products.detail(productId),
queryFn: () => productService.getProduct(productId),
});
// Prefetch related data in parallel
await Promise.all([
queryClient.prefetchQuery({
queryKey: queryKeys.products.reviews(productId),
queryFn: () => productService.getProductReviews(productId),
}),
queryClient.prefetchQuery({
queryKey: queryKeys.products.related(productId),
queryFn: () => productService.getRelatedProducts(productId),
}),
]);
// Navigate after data is in cache
navigation.navigate('ProductDetail', { productId });
};
```
## Testing Server State
```typescript
// features/products/api/__tests__/productHooks.test.ts
import { renderHook, waitFor } from '@testing-library/react-native';
import { useProductsQuery } from '../productHooks';
import { productService } from '@/core/api/services';
import { createWrapper } from '@/test/utils';
jest.mock('@/core/api/services');
describe('useProductsQuery', () => {
it('should fetch products successfully', async () => {
const mockProducts = [{ id: '1', name: 'Test Product' }];
(productService.getProducts as jest.Mock).mockResolvedValue(mockProducts);
const { result } = renderHook(() => useProductsQuery(), {
wrapper: createWrapper(),
});
expect(result.current.isLoading).toBe(true);
await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});
expect(result.current.data).toEqual(mockProducts);
});
it('should handle errors', async () => {
const error = new Error('Failed to fetch');
(productService.getProducts as jest.Mock).mockRejectedValue(error);
const { result } = renderHook(() => useProductsQuery(), {
wrapper: createWrapper(),
});
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toEqual(error);
});
});
```
```typescript
// test/utils/createWrapper.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { PropsWithChildren } from 'react';
export function createWrapper() {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
cacheTime: 0,
},
},
logger: {
log: console.log,
warn: console.warn,
error: () => {}, // Silent errors in tests
},
});
return ({ children }: PropsWithChildren<{}>) => (
{children}
);
}
```
```typescript
// features/products/screens/__tests__/ProductListScreen.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react-native';
import { ProductListScreen } from '../ProductListScreen';
import { useProductsQuery } from '../../api/productHooks';
import { createWrapper } from '@/test/utils';
jest.mock('../../api/productHooks');
describe('ProductListScreen', () => {
it('should render products and handle item press', async () => {
const mockProducts = [
{ id: '1', name: 'Product 1', price: '$10' },
{ id: '2', name: 'Product 2', price: '$20' },
];
(useProductsQuery as jest.Mock).mockReturnValue({
data: mockProducts,
isLoading: false,
isError: false,
});
const mockNavigate = jest.fn();
jest.mock('@react-navigation/native', () => ({
useNavigation: () => ({
navigate: mockNavigate,
}),
}));
const { getByText } = render(, {
wrapper: createWrapper(),
});
expect(getByText('Product 1')).toBeTruthy();
expect(getByText('Product 2')).toBeTruthy();
fireEvent.press(getByText('Product 1'));
await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith('ProductDetail', { productId: '1' });
});
});
});
```
## Troubleshooting
1. **Stale Data Persisting**: If stale data persists longer than expected, check your `staleTime` configuration. You might need to adjust it for your specific use case.
2. **Multiple Network Requests**: If you're seeing duplicate requests, check the query key construction. Ensure keys are consistent and deterministic across renders.
3. **Memory Leaks**: When working with infinite queries or large data sets, monitor memory usage. Consider implementing pagination or windowing for large lists.
4. **Background Refetching Issues**: If background refetching isn't working as expected, verify `refetchOnWindowFocus` and `refetchOnMount` settings.
The React Query Devtools are essential for debugging queries in development:
```typescript
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools/build/web';
function App() {
return (
{/* Your application */}
{__DEV__ && }
);
}
```
For React Native, consider using a custom logging middleware or Flipper plugin for TanStack Query.
If you encounter performance issues:
1. **Enable Selective Updates**: Use the `select` option to transform and filter data, reducing component re-renders.
2. **Use Query Cancellation**: Cancel pending queries when they're no longer needed.
3. **Optimize Query Keys**: Structure keys appropriately to maximize cache hits.
4. **Analyze QueryClient Configuration**: Review `cacheTime`, `staleTime`, and garbage collection settings.
## Related Documents
Overview of our complete state management approach including client, server, and component state.
Companion documentation for handling client-specific state with Zustand.
Patterns for integrating server and client state effectively.
setCount(c => c + 1)}>IncrementShow alert
>
);
}
```
2. **Batched Updates**: Multiple state updates in event handlers are batched
```typescript
// ❌ Problem: Both setState calls only trigger one re-render
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // count is 0
setCount(count + 1); // count is still 0, so this sets it to 1 again
};
return Count: {count};
}
// ✅ Solution: Use functional updates
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(c => c + 1); // c is 0, sets to 1
setCount(c => c + 1); // c is 1, sets to 2
};
return Count: {count};
}
```
1. **Memory Leaks**: Not cleaning up effects properly
```typescript
// ❌ Problem: No cleanup causes memory leaks
function UserStatus() {
const [isOnline, setIsOnline] = useState(false);
useEffect(() => {
const subscription = subscribeToUserStatus(userId, (status) => {
setIsOnline(status.isOnline);
});
// No cleanup, subscription stays active after unmount
}, [userId]);
return
{isOnline ? 'Online' : 'Offline'}
;
}
// ✅ Solution: Proper cleanup function
function UserStatus() {
const [isOnline, setIsOnline] = useState(false);
useEffect(() => {
const subscription = subscribeToUserStatus(userId, (status) => {
setIsOnline(status.isOnline);
});
// Cleanup function that runs before next effect or on unmount
return () => {
subscription.unsubscribe();
};
}, [userId]);
return
{isOnline ? 'Online' : 'Offline'}
;
}
```
1. **Unnecessary Re-renders**: Components re-render when props or state haven't changed
```typescript
// ❌ Problem: New function reference on every render
function ParentComponent() {
const [count, setCount] = useState(0);
// This creates a new function reference every render
const handleClick = () => {
console.log('Clicked');
};
return ;
}
// ✅ Solution: Memoize function with useCallback
function ParentComponent() {
const [count, setCount] = useState(0);
// Stable function reference between renders
const handleClick = useCallback(() => {
console.log('Clicked');
}, []); // Only changes if dependencies change
return ;
}
```
## Related Documents
Overview of our complete state management approach including client, server, and component state.
Companion documentation for handling global client state with Zustand.
Companion documentation for handling server state with TanStack Query.