This document outlines the architectural boundaries and import rules enforced by ESLint in the Flow Reference Wallet Chrome extension.
The codebase is organized into distinct layers with strict import rules to maintain separation of concerns and prevent architectural violations. Each folder has specific responsibilities and controlled dependencies.
src/
├── background/ # Chrome extension background scripts
├── core/ # Core business logic and services
├── ui/ # Popup UI components and views
│ ├── components/ # Reusable UI components
│ ├── views/ # Page-level components and screens
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # UI-specific utilities
│ ├── assets/ # Static assets (images, fonts, etc.)
│ └── style/ # Styling and theming
├── content-script/ # Web page content scripts
└── packages/ # Workspace packages
├── shared/ # Shared utilities and types
└── reducers/ # State management reducers
graph TB
subgraph "Chrome Extension Context"
subgraph "Background Service Worker"
BG[background/]
CORE[core/]
BG --> CORE
end
subgraph "UI Context"
UI[ui/]
subgraph "UI Internal Structure"
VIEWS[views/]
COMPONENTS[components/]
HOOKS[hooks/]
REDUCERS[hooks use reducers package]
UI_UTILS[utils/]
ASSETS[assets/]
STYLE[style/]
VIEWS --> COMPONENTS
VIEWS --> HOOKS
COMPONENTS --> HOOKS
HOOKS -.->|messaging| BG
HOOKS --> SHARED
end
end
subgraph "Content Script Context"
CS[content-script/]
end
subgraph "Workspace Packages"
SHARED[shared/]
REDUCERS[reducers/]
end
end
%% Dependencies
UI -.->|messaging| BG
CS --> BG
BG --> SHARED
CORE --> SHARED
UI --> SHARED
UI --> REDUCERS
CS --> SHARED
HOOKS --> REDUCERS
%% Styling
classDef background fill:#e1f5fe
classDef core fill:#f3e5f5
classDef ui fill:#e8f5e8
classDef uiInternal fill:#c8e6c9
classDef contentScript fill:#fff3e0
classDef shared fill:#f5f5f5
classDef reducers fill:#e3f2fd
class BG background
class CORE core
class UI ui
class VIEWS,COMPONENTS,HOOKS,UI_UTILS,ASSETS,STYLE uiInternal
class CS contentScript
class SHARED shared
class REDUCERS reducers
- Purpose: Chrome extension background service worker
- Responsibilities:
- Extension lifecycle management
- Message routing between UI and core
- Chrome API interactions
- Wallet controller coordination
- Can import from:
@onflow/frw-core/*,@onflow/frw-shared/* - Cannot import from:
@/ui/*
- Purpose: Core business logic and services
- Responsibilities:
- Wallet services (keyring, transactions, etc.)
- Blockchain interactions
- Business logic
- Data persistence
- Can import from:
@onflow/frw-shared/*,@/background/webapi/* - Cannot import from:
@/ui/*,@/background/*(except webapi)
- Purpose: Popup interface and user interactions
- Responsibilities:
- React components and views
- User interface logic
- State management for display
- User input handling
- Can import from:
@onflow/frw-shared/* - Cannot import from:
@onflow/frw-core/*,@/background/* - Communication: Uses messaging to communicate with background/wallet controller
- Purpose: Page-level components and complete screens
- Responsibilities:
- Route components
- Page layouts
- Screen orchestration
- Complex user flows
- Can import from:
@/ui/components/*,@/ui/hooks/*,@/ui/utils/*,@/ui/assets/*,@/ui/style/*,@onflow/frw-shared/*,@onflow/frw-reducers/* - Import pattern: Views compose components and use hooks for state management
- Purpose: Reusable UI components
- Responsibilities:
- Atomic UI elements
- Reusable component patterns
- Component composition
- UI interactions
- Can import from:
@/ui/hooks/*,@/ui/utils/*,@/ui/assets/*,@/ui/style/*,@onflow/frw-shared/*,@onflow/frw-reducers/* - Cannot import from:
@/ui/views/*(components should not depend on views)
- Purpose: Custom React hooks for state and side effects
- Responsibilities:
- State management logic
- Background communication
- Storage interactions
- Custom React patterns
- Can import from:
@/ui/utils/*,@onflow/frw-shared/*,@onflow/frw-reducers/* - Special permissions: Can communicate with background via messaging
- Cannot import from:
@/ui/views/*,@/ui/components/*
- Purpose: UI-specific utility functions
- Responsibilities:
- UI helper functions
- Formatting utilities
- UI-specific calculations
- Browser API wrappers
- Can import from:
@onflow/frw-shared/* - Cannot import from: Other UI subfolders (to prevent circular dependencies)
- Purpose: Static assets for the UI
- Responsibilities:
- Images, icons, fonts
- Static JSON data
- SVG assets
- Import pattern: Imported by other UI components using
@/ui/assets/*
- Purpose: Styling and theming
- Responsibilities:
- Theme definitions
- CSS utilities
- Design tokens
- Global styles
- Can import from:
@onflow/frw-shared/*(for shared constants) - Import pattern: Imported by components for styling
- Purpose: Web page integration
- Responsibilities:
- dApp provider injection
- Web page communication
- Flow FCL and Ethereum provider interfaces
- Can import from:
@/background/*,@onflow/frw-shared/*
- Purpose: State management and data transformations (now as a separate workspace package)
- Responsibilities:
- State reduction logic
- Data transformations
- Action handling
- Pure state functions
- Location:
packages/reducers/ - Import as:
@onflow/frw-reducers/* - Can import from:
@onflow/frw-shared/*only - Cannot import from: Any project folders or other packages
- Note: Must remain pure and isolated for predictable state management
- Purpose: Common utilities and types (now as a separate workspace package)
- Responsibilities:
- Type definitions
- Utility functions
- Constants
- Common helpers
- Location:
packages/shared/ - Import as:
@onflow/frw-shared/* - Can import from: Nothing (standalone package)
- Cannot import from: Any project folders
- Use aliases for cross-folder imports: Always use
@/folder/*syntax - Shared package imports: Use
@onflow/frw-shared/* - Layer boundaries are enforced: UI ↔ Core/Background separation
- Relative imports allowed within folders: Use
./or../within the same folder
// ✅ Correct - importing from shared package
import { SomeType } from '@onflow/frw-shared/types';
import { formatAddress } from '@onflow/frw-shared/utils';
// ✅ Correct - cross-folder imports with aliases
import { walletService } from '@onflow/frw-core/service/wallet';
import { Button } from '@/ui/components/Button';
import { useWallet } from '@/ui/hooks/use-wallet';
// ✅ Correct - relative imports within same folder
import { helper } from './helper';
import { utils } from '../utils';Use relative imports only within the same subfolder:
// ✅ Correct - within same subfolder
import { helper } from './helper';
import { Component } from '../SubFolder/Component';
import { utils } from '../../utils/index';// ✅ Views can import components
import { AccountCard } from '@/ui/components/account/account-card';
// ✅ Components can import hooks
import { useWallet } from '@/ui/hooks/use-wallet';
// ✅ Hooks can import reducers from package
import { accountReducer } from '@onflow/frw-reducers/account-reducer';
// ✅ Anyone can import from shared
import { formatAddress } from '@onflow/frw-shared/utils';// ❌ Wrong - UI importing from core
import { keyringService } from '@onflow/frw-core/service/keyring';
// ❌ Wrong - Core importing from UI
import { Button } from '@/ui/components/Button';
// ❌ Wrong - Background importing from UI
import { useWallet } from '@/ui/hooks/use-wallet';
// ❌ Wrong - Components importing reducers from old location
import { accountReducer } from '@/ui/reducers/account-reducer';
// ✅ Correct - Import from the package
import { accountReducer } from '@onflow/frw-reducers/account-reducer';
// ❌ Wrong - Using old shared path
import { formatAddress } from '@/shared/utils/address';
// ✅ Correct - Use the package name
import { formatAddress } from '@onflow/frw-shared/utils';UI communicates with background through Chrome extension messaging:
// UI hooks
export const useWallet = () => {
const walletController = new WalletController();
const getAccounts = async () => {
return await walletController.getAccounts();
};
return { getAccounts };
};
// Background side
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Handle messages from UI
});// Views use components and hooks
const Dashboard = () => {
const { accounts } = useWallet(); // Hook for data
return (
<div>
<AccountCard account={accounts[0]} /> {/* Component for UI */}
</div>
);
};
// Hooks use reducers from the package for state management
import { walletReducer } from '@onflow/frw-reducers/wallet-reducer';
const useWallet = () => {
const [state, dispatch] = useReducer(walletReducer, initialState);
// ...
};Background directly imports and uses core services:
// Background
import { keyringService } from '@onflow/frw-core/service/keyring';
const accounts = await keyringService.getAccounts();The ESLint rules have been simplified to focus on the essential architectural boundaries:
-
Background folder:
- Cannot import from
@/ui/* - All other imports allowed
- Cannot import from
-
Core folder:
- Cannot import from
@/ui/* - Can only import
@/background/webapi/*from background - All other imports allowed
- Cannot import from
-
Core services:
- Same as core, plus:
- Can only import from
@onflow/frw-core/service/*or@onflow/frw-core/utils/*within core
-
UI folder:
- Cannot import from
@onflow/frw-core/*or@/background/* - All other imports allowed
- Cannot import from
-
Reducers package (
@onflow/frw-reducers):- Can only import from
@onflow/frw-shared/* - Must remain pure (no imports from other layers or packages)
- Can only import from
-
Content script:
- Cannot import from
@/ui/*or@onflow/frw-core/* - Can import from
@/background/*and shared
- Cannot import from
This architecture provides:
- Clear separation of concerns: Each layer has distinct responsibilities
- Maintainable codebase: Easier to understand and modify
- Testable code: Isolated layers can be tested independently
- Chrome extension compliance: Proper separation between contexts
- Security: Background service worker isolation
- Scalability: Clear boundaries for future development
- Predictable state management: Isolated reducers ensure predictable state changes
- Reusable components: Components can be used across different views
- Organized UI structure: Clear hierarchy from views → components → hooks → reducers
- Before adding imports: Check if the import follows the architectural rules
- Use aliases: Always use
@/folder/*aliases for cross-folder imports - Use package imports: Import from
@onflow/frw-shared/*and@onflow/frw-reducers/* - Keep layers isolated: Don't create direct dependencies between UI and core
- Use messaging: UI should communicate with core through background messaging
- Shared utilities: Put common code in shared package, not in specific layers
- UI hierarchy: Follow views → components → hooks pattern (reducers are now separate)
- Pure reducers: Reducers must remain in the separate package and stay pure
- Reusable components: Design components to be reusable across different views
If you encounter ESLint violations:
- "UI cannot import from Core layer": Use the wallet controller messaging instead
- "Core cannot import from UI layer": Core should not depend on UI components
- "Background cannot import from UI layer": Background is a service worker without DOM access
- "Reducers must be pure": Reducers are now in a separate package and can only import from shared
- "Core services can only import webapi from background": Use the webapi module for browser APIs
- "Cannot import reducers from old location": Import from
@onflow/frw-reducers/*instead
This architecture ensures a maintainable, secure, and scalable Chrome extension codebase with clear separation of concerns at both the application and UI levels.