Communication Patterns
Guide to communication patterns between features, domains, and system components.
Communication Patterns
Overview
This document outlines the various communication patterns available in our architecture for cross-feature and cross-domain communication. It provides guidelines on when and how to use each pattern while maintaining clean module boundaries.
Purpose & Scope
This guide helps developers understand:
- Available communication methods
- When to use each pattern
- Implementation examples
- Best practices for maintaining loose coupling
Communication Overview
Our architecture supports four primary communication patterns:
Domain Services
Shared hooks and API services for accessing domain data across features.
Event System
Publish-subscribe pattern for loose coupling between components.
Shared State
Server state via TanStack Query and client state via Zustand.
Navigation Parameters
Passing data between screens through navigation parameters.
- Domain Services - Shared hooks and API services
- Event System - Publish-subscribe pattern
- Shared State - Query cache and global state
- Navigation Parameters - Data passing through navigation
Pattern 1: Domain Services
Overview
Features access shared business logic through domain hooks and services, maintaining a clean separation between UI and business logic.
When to Use
- Accessing domain data (products, users, orders)
- Performing business operations
- Sharing data between features
Benefits
- Type-safe data access
- Shared caching through TanStack Query
- Consistent data across features
- Clear separation of concerns
Implementation
Benefits
- Type-safe data access
- Shared caching through TanStack Query
- Consistent data across features
Pattern 2: Event System
Overview
A publish-subscribe pattern for loose coupling between components that shouldn't directly depend on each other.
When to Use
- System-wide notifications
- Authentication state changes
- Real-time updates
- Cross-domain reactions
Benefits
- Loose coupling between components
- Flexibility to add new subscribers
- Supports one-to-many communication
- Allows cross-feature coordination
Implementation
Benefits
- Loose coupling between components
- System-wide event handling
- Clean separation of concerns
Pattern 3: Shared State
Overview
Shared state includes both server state via TanStack Query's caching layer and client state via Zustand stores.
When to Use
- Persistent data from API responses (TanStack Query)
- Global UI state (modals, themes, etc.)
- User preferences
- Cached data needed across features
Benefits
- Automatic data synchronization
- Built-in caching and invalidation
- Persistent state across app reloads
- Optimized rendering performance for state that needs to be accessible across features.
- Session information
Implementation
Via Query Cache
Via Global Store
Benefits
- Efficient data sharing
- Automatic synchronization
- Reduced API calls
Pattern 4: Navigation Parameters
Overview
Passing data between screens through navigation parameters.
When to Use
- Screen-to-screen data transfer
- Deep linking with parameters
- Temporary state during navigation flow
Benefits
- Decoupled screen implementations
- Support for deep linking
- Type-safe data passing
- Maintains clear navigation history
Implementation
Benefits
- Type-safe parameter passing
- Works with deep linking
- Clear data flow
Choosing the Right Pattern
Use this decision tree to select the appropriate communication pattern:
Anti-Patterns to Avoid
1. Direct Feature Imports
❌ Wrong: Features importing from each other
✅ Correct: Use shared UI components
2. Prop Drilling
❌ Wrong: Passing data through multiple levels
✅ Correct: Use appropriate state management
3. Circular Dependencies
❌ Wrong: Domains depending on each other
✅ Correct: Compose in features
Best Practices
1. Keep Communication Minimal
- Only share what's necessary
- Prefer local state when possible
- Avoid over-engineering
2. Document Communication Points
3. Type Everything
4. Handle Errors Gracefully
Examples by Use Case
User Authentication Flow
Real-time Product Updates
Testing Communication
Testing Events
Testing Navigation
Design Principles
Core Architectural Principles
-
Loose Coupling
- Features should not be directly dependent on each other
- Communication should occur through well-defined interfaces
- Implementation details should be hidden behind abstractions
-
Clear Boundaries
- Feature modules must maintain strict boundaries
- Communication across boundaries should be explicit and controlled
- No circular dependencies between features
-
Single Responsibility
- Each communication pattern serves a specific purpose
- The pattern chosen should match the type of data being shared
- Complex flows should be decomposed into manageable parts
Trade-offs and Design Decisions
| Pattern | Benefits | Drawbacks | When To Choose |
|---|---|---|---|
| Domain Services | Type-safe, shared caching | Can create domain dependencies | For domain data that needs persistence |
| Event System | Loose coupling, flexible | Harder to trace, debug | For system-wide notifications |
| Shared State | Centralized, reactive | Potential for overuse | For truly global state |
| Navigation | Simple, direct | Limited to screen flow | For screen-to-screen transitions |
Constraints and Considerations
- Features must never import directly from other features
- Communication patterns should be used consistently across the app
- Performance implications must be considered for each pattern
- Type safety should be maintained across all communication boundaries
Implementation Considerations
Performance Implications
- Domain Services: Use staleTime and cacheTime to optimize refetching
- Event System: Limit subscribers to avoid memory leaks
- Shared State: Be selective about what goes into global state
- Navigation: Pass IDs rather than large objects between screens
Security Considerations
- Domain Services: Validate data at domain boundaries
- Event System: Don't emit sensitive data in events
- Shared State: Be cautious about storing sensitive information
- Navigation: Sanitize parameters from deep links
Scalability Aspects
- Domain Services: Scale well with additional domains
- Event System: Use namespaced events for growing systems
- Shared State: Split into domain-specific stores as app grows
- Navigation: Use nested navigators for complex flows
Summary
Our communication patterns provide flexible options for different scenarios:
- Domain Services: Primary method for data access
- Events: System-wide notifications and reactions
- Shared State: Cached data and global state
- Navigation: Screen-to-screen data passing
Choose the simplest pattern that meets your needs and maintain clear boundaries between features.
Related Documents
- Core Architecture - Overview of the application's hybrid architecture
- Project Structure - Directory structure and organization
- State Management - Detailed state management patterns
- API Client Architecture - API client implementation patterns
- testing