Create comprehensive Obsidian-style documentation structure
- Reorganize all markdown documentation into structured docs/ folder - Create 7 main documentation categories (00-overview through 06-deployment) - Add comprehensive index files for each category with cross-linking - Implement Obsidian-compatible [[link]] syntax throughout - Move legacy/deprecated documentation to archive folder - Establish documentation standards and maintenance guidelines - Provide complete coverage of modular architecture, services, and deployment - Enable better navigation and discoverability for developers and contributors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
46856134ef
commit
cdf099e45f
29 changed files with 3733 additions and 0 deletions
241
docs/01-architecture/index.md
Normal file
241
docs/01-architecture/index.md
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
# 🏗️ Architecture Overview
|
||||
|
||||
> **System design and patterns** powering Ario's modular, scalable, and maintainable architecture with dependency injection, plugin-based modules, and reactive services.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [[#Architecture Principles]]
|
||||
- [[#Core Systems]]
|
||||
- [[#Service Architecture]]
|
||||
- [[#Module System]]
|
||||
- [[#Communication Patterns]]
|
||||
- [[#Data Flow]]
|
||||
|
||||
## Architecture Principles
|
||||
|
||||
### **Modularity** - Plugin-Based Design
|
||||
Every feature is implemented as a self-contained module that can be enabled, disabled, or configured independently. This approach ensures:
|
||||
- **Separation of Concerns** - Each module owns its domain logic
|
||||
- **Testability** - Modules can be tested in isolation
|
||||
- **Maintainability** - Changes to one module don't affect others
|
||||
- **Extensibility** - New features can be added as plugins
|
||||
|
||||
### **Loose Coupling** - Dependency Injection
|
||||
Services communicate through well-defined interfaces using a dependency injection container:
|
||||
- **Interface-Based Design** - Services depend on abstractions, not implementations
|
||||
- **Runtime Configuration** - Service instances can be swapped for testing or different environments
|
||||
- **Clear Dependencies** - Service relationships are explicit and manageable
|
||||
- **Lifecycle Management** - Services have defined initialization and disposal phases
|
||||
|
||||
### **Reactive Architecture** - Vue-Powered Services
|
||||
Services integrate Vue's reactivity system for state management:
|
||||
- **Reactive State** - Services expose reactive refs and computed properties
|
||||
- **Automatic Updates** - UI automatically updates when service state changes
|
||||
- **Event-Driven Communication** - Services communicate via event bus patterns
|
||||
- **Consistent Patterns** - Same reactivity patterns used throughout the application
|
||||
|
||||
## Core Systems
|
||||
|
||||
### **Dependency Injection Container**
|
||||
Location: `src/core/di-container.ts`
|
||||
|
||||
Centralized service registry and resolution system:
|
||||
```typescript
|
||||
// Service registration (in modules)
|
||||
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
|
||||
|
||||
// Service consumption (anywhere)
|
||||
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- Type-safe service tokens
|
||||
- Singleton lifecycle management
|
||||
- Runtime service validation
|
||||
- Development-time debugging support
|
||||
|
||||
### **Plugin Manager**
|
||||
Location: `src/core/plugin-manager.ts`
|
||||
|
||||
Orchestrates module loading and dependency resolution:
|
||||
```typescript
|
||||
class PluginManager {
|
||||
async loadModule(plugin: ModulePlugin): Promise<void>
|
||||
getDependencyGraph(): Map<string, string[]>
|
||||
validateDependencies(modules: ModulePlugin[]): void
|
||||
}
|
||||
```
|
||||
|
||||
### **Base Service Architecture**
|
||||
Location: `src/core/base/BaseService.ts`
|
||||
|
||||
Abstract foundation for all services with common patterns:
|
||||
```typescript
|
||||
abstract class BaseService {
|
||||
protected isInitialized = ref(false)
|
||||
protected isDisposed = ref(false)
|
||||
|
||||
abstract initialize(): Promise<void>
|
||||
abstract dispose(): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
## Service Architecture
|
||||
|
||||
### **Service Categories**
|
||||
|
||||
#### **Infrastructure Services** (Base Module)
|
||||
- **RelayHub** - Nostr relay connection management
|
||||
- **AuthService** - User authentication and key management
|
||||
- **StorageService** - User-scoped local storage operations
|
||||
- **ToastService** - User notifications and feedback
|
||||
|
||||
#### **Feature Services** (Module-Specific)
|
||||
- **ChatService** - Encrypted messaging functionality
|
||||
- **MarketService** - Marketplace operations
|
||||
- **EventsService** - Event creation and ticketing
|
||||
- **FeedService** - Social feed management
|
||||
|
||||
### **Service Communication Patterns**
|
||||
|
||||
#### **Direct Service Dependencies**
|
||||
For tightly coupled operations:
|
||||
```typescript
|
||||
class ChatService extends BaseService {
|
||||
constructor(
|
||||
private relayHub = injectService(SERVICE_TOKENS.RELAY_HUB),
|
||||
private auth = injectService(SERVICE_TOKENS.AUTH_SERVICE)
|
||||
) {}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Event Bus Communication**
|
||||
For loose coupling between modules:
|
||||
```typescript
|
||||
// Publishing events
|
||||
eventBus.emit('user:authenticated', { userId: user.pubkey })
|
||||
|
||||
// Subscribing to events
|
||||
eventBus.on('payment:received', handlePaymentReceived)
|
||||
```
|
||||
|
||||
## Module System
|
||||
|
||||
### **Module Plugin Structure**
|
||||
Each module implements the `ModulePlugin` interface:
|
||||
```typescript
|
||||
interface ModulePlugin {
|
||||
name: string
|
||||
version: string
|
||||
dependencies: string[]
|
||||
|
||||
install(app: App, options?: any): Promise<void>
|
||||
routes?: RouteRecordRaw[]
|
||||
components?: Record<string, Component>
|
||||
}
|
||||
```
|
||||
|
||||
### **Module Categories**
|
||||
|
||||
#### **Base Module** - Core Infrastructure
|
||||
- **Purpose** - Provides shared services and infrastructure
|
||||
- **Dependencies** - None (foundation module)
|
||||
- **Exports** - RelayHub, AuthService, StorageService, ToastService
|
||||
|
||||
#### **Feature Modules** - Domain-Specific Logic
|
||||
- **Chat Module** - Encrypted messaging with Nostr DMs
|
||||
- **Events Module** - Event creation and Lightning ticketing
|
||||
- **Market Module** - Nostr marketplace functionality
|
||||
- **Nostr Feed Module** - Social feed and content discovery
|
||||
|
||||
### **Module Configuration**
|
||||
Location: `src/app.config.ts`
|
||||
```typescript
|
||||
export const moduleConfigs = {
|
||||
'base': { enabled: true },
|
||||
'chat': { enabled: true, maxMessageLength: 1000 },
|
||||
'events': { enabled: true, defaultCurrency: 'sat' },
|
||||
'market': { enabled: true, allowedCategories: ['all'] },
|
||||
'nostr-feed': { enabled: true, defaultRelays: [] }
|
||||
}
|
||||
```
|
||||
|
||||
## Communication Patterns
|
||||
|
||||
### **Service-to-Service Communication**
|
||||
```typescript
|
||||
// 1. Direct injection for required dependencies
|
||||
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
|
||||
|
||||
// 2. Event bus for optional/cross-module communication
|
||||
eventBus.emit('user:profile-updated', profileData)
|
||||
|
||||
// 3. Reactive refs for state sharing
|
||||
const connectionStatus = computed(() => relayHub.connectionStatus.value)
|
||||
```
|
||||
|
||||
### **Component-to-Service Communication**
|
||||
```typescript
|
||||
// 1. Composables wrap service access
|
||||
const { publishNote, isPublishing } = useNoteComposer()
|
||||
|
||||
// 2. Direct service injection in components
|
||||
const toastService = injectService(SERVICE_TOKENS.TOAST_SERVICE)
|
||||
|
||||
// 3. Store integration for component state
|
||||
const authStore = useAuthStore()
|
||||
```
|
||||
|
||||
### **Module-to-Module Communication**
|
||||
```typescript
|
||||
// ❌ Direct imports break modularity
|
||||
import { chatService } from '../chat/services/chat-service'
|
||||
|
||||
// ✅ Dependency injection maintains loose coupling
|
||||
const chatService = injectService(SERVICE_TOKENS.CHAT_SERVICE)
|
||||
|
||||
// ✅ Event bus for cross-module notifications
|
||||
eventBus.emit('chat:message-received', messageData)
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
### **Nostr Event Lifecycle**
|
||||
1. **Event Creation** - User action triggers event creation
|
||||
2. **Event Signing** - AuthService signs event with user's private key
|
||||
3. **Relay Publishing** - RelayHub publishes to configured relays
|
||||
4. **Event Storage** - StorageService caches event locally
|
||||
5. **UI Updates** - Reactive properties trigger component re-renders
|
||||
|
||||
### **Lightning Payment Flow**
|
||||
1. **Invoice Generation** - EventsService creates Lightning invoice
|
||||
2. **QR Code Display** - UI presents payment interface
|
||||
3. **Payment Detection** - WebLN or external wallet processes payment
|
||||
4. **Verification** - Service confirms payment via Lightning backend
|
||||
5. **State Updates** - Payment success triggers relevant service updates
|
||||
|
||||
### **Module Initialization Flow**
|
||||
1. **Dependency Resolution** - PluginManager sorts modules by dependencies
|
||||
2. **Service Registration** - Each module registers services in DI container
|
||||
3. **Route Registration** - Module routes added to Vue Router
|
||||
4. **Component Registration** - Global components made available
|
||||
5. **Service Initialization** - Services initialize in dependency order
|
||||
|
||||
## See Also
|
||||
|
||||
### Deep Dive Documentation
|
||||
- **[[modular-design|📦 Modular Design Patterns]]** - Plugin architecture details
|
||||
- **[[dependency-injection|⚙️ Dependency Injection]]** - Service container system
|
||||
- **[[relay-hub|🌐 Relay Hub Architecture]]** - Nostr relay management
|
||||
- **[[event-bus|📡 Event Bus Communication]]** - Inter-module messaging
|
||||
|
||||
### Implementation References
|
||||
- **[[../02-modules/index|📦 Module Documentation]]** - Individual module guides
|
||||
- **[[../03-core-services/index|⚙️ Core Services]]** - Service implementations
|
||||
- **[[../04-development/index|💻 Development Guide]]** - Architectural patterns in practice
|
||||
|
||||
---
|
||||
|
||||
**Tags:** #architecture #design-patterns #modularity #dependency-injection
|
||||
**Last Updated:** 2025-09-06
|
||||
**Author:** Development Team
|
||||
419
docs/01-architecture/modular-design.md
Normal file
419
docs/01-architecture/modular-design.md
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
# Modular Architecture Analysis & Infrastructure Abstractions
|
||||
|
||||
**Project**: Ario Web Application
|
||||
**Date**: September 5, 2025
|
||||
**Scope**: Analysis of modular plugin architecture for centralization opportunities
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The Ario web application demonstrates a well-architected modular system using Vue 3, TypeScript, and dependency injection. After comprehensive analysis of all modules, we've identified **significant opportunities for code consolidation** that could reduce duplication by 30-40% while improving maintainability, type safety, and development velocity.
|
||||
|
||||
**Key Findings**:
|
||||
- **16+ duplicate patterns** across modules requiring abstraction
|
||||
- **Common async operation pattern** found in 12+ files
|
||||
- **Service dependency injection** pattern duplicated in 7+ services
|
||||
- **Payment processing logic** duplicated between market and events modules
|
||||
- **User-scoped storage** pattern repeated across multiple stores
|
||||
|
||||
**Recent Progress (September 6, 2025)**:
|
||||
- ✅ **Legacy relay infrastructure cleanup completed**
|
||||
- ✅ Removed redundant NostrclientHub service
|
||||
- ✅ Removed legacy NostrStore Pinia store
|
||||
- ✅ Standardized on RelayHub service for all Nostr operations
|
||||
- ✅ Updated dependency injection to remove unused tokens
|
||||
|
||||
---
|
||||
|
||||
## Current Module Structure
|
||||
|
||||
### Base Module (`src/modules/base/`)
|
||||
- **Purpose**: Core infrastructure (Nostr, Auth, PWA)
|
||||
- **Key Services**: Authentication, Relay Hub, Payment Service, Visibility Service
|
||||
- **Status**: Well-established foundation with recent cleanup of legacy relay infrastructure
|
||||
|
||||
### Chat Module (`src/modules/chat/`)
|
||||
- **Purpose**: Encrypted Nostr chat functionality
|
||||
- **Key Features**: NIP-04 encryption, DM handling, peer management
|
||||
- **Dependencies**: Base module services
|
||||
|
||||
### Events Module (`src/modules/events/`)
|
||||
- **Purpose**: Event ticketing with Lightning payments
|
||||
- **Key Features**: Ticket purchase, Lightning integration, QR codes
|
||||
- **Dependencies**: Base module, payment processing
|
||||
|
||||
### Market Module (`src/modules/market/`)
|
||||
- **Purpose**: Nostr marketplace with order management
|
||||
- **Key Features**: Product catalog, order processing, payment handling
|
||||
- **Dependencies**: Base module, payment processing, storage
|
||||
|
||||
### Nostr Feed Module (`src/modules/nostr-feed/`)
|
||||
- **Purpose**: Social feed functionality
|
||||
- **Key Features**: Note filtering, admin posts, real-time updates
|
||||
- **Dependencies**: Base module services
|
||||
|
||||
---
|
||||
|
||||
## Identified Duplicate Patterns
|
||||
|
||||
### 1. Async Operation Pattern ⭐ **HIGH PRIORITY**
|
||||
|
||||
**Occurrences**: Found in **12+ files** across all modules
|
||||
|
||||
**Current Implementation**:
|
||||
```typescript
|
||||
// Repeated in every composable/component
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const data = ref<T | null>(null)
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
// operation
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- 200+ lines of duplicate code
|
||||
- Inconsistent error handling
|
||||
- Manual loading state management
|
||||
|
||||
### 2. Service Dependency Injection ⭐ **HIGH PRIORITY**
|
||||
|
||||
**Occurrences**: Found in **7+ service files**
|
||||
|
||||
**Current Implementation**:
|
||||
```typescript
|
||||
// Repeated in every service
|
||||
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB) as any
|
||||
const authService = injectService(SERVICE_TOKENS.AUTH_SERVICE) as any
|
||||
|
||||
if (!relayHub) {
|
||||
throw new Error('RelayHub not available')
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- Type safety issues (`as any` casting)
|
||||
- Duplicate dependency checking
|
||||
- Inconsistent error messages
|
||||
|
||||
### 3. Payment Processing Logic ⭐ **HIGH PRIORITY**
|
||||
|
||||
**Occurrences**: Duplicated between market and events modules
|
||||
|
||||
**Current Implementation**:
|
||||
```typescript
|
||||
// Similar logic in both modules
|
||||
const payInvoiceWithWallet = async (bolt11: string) => {
|
||||
// Lightning payment logic
|
||||
// QR code generation
|
||||
// Success/error handling
|
||||
// Toast notifications
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- 150+ lines of duplicate payment logic
|
||||
- Inconsistent payment UX
|
||||
- Duplicate error handling
|
||||
|
||||
### 4. User-Scoped Storage Pattern
|
||||
|
||||
**Occurrences**: Found in market store, chat service
|
||||
|
||||
**Current Implementation**:
|
||||
```typescript
|
||||
// Pattern repeated across modules
|
||||
const getUserStorageKey = (baseKey: string) => {
|
||||
const userPubkey = auth.currentUser?.value?.pubkey
|
||||
return userPubkey ? `${baseKey}_${userPubkey}` : baseKey
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- Duplicate storage key logic
|
||||
- Potential data consistency issues
|
||||
|
||||
### 5. Module Registration Pattern
|
||||
|
||||
**Occurrences**: Every module (`index.ts` files)
|
||||
|
||||
**Current Implementation**:
|
||||
```typescript
|
||||
// Identical structure in every module
|
||||
export const modulePlugin: ModulePlugin = {
|
||||
name: 'module-name',
|
||||
install(app: App, options) {
|
||||
// Service registration
|
||||
// Component registration
|
||||
// Route registration
|
||||
// Event setup
|
||||
},
|
||||
uninstall() {
|
||||
// Cleanup logic
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- Boilerplate code in every module
|
||||
- Inconsistent registration patterns
|
||||
|
||||
### 6. Toast Notification Pattern
|
||||
|
||||
**Occurrences**: Found in 4+ files
|
||||
|
||||
**Current Implementation**:
|
||||
```typescript
|
||||
// Inconsistent usage across modules
|
||||
toast.success('Operation successful!')
|
||||
toast.error('Operation failed')
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- Inconsistent notification styling
|
||||
- Duplicate notification logic
|
||||
|
||||
---
|
||||
|
||||
## Recommended Abstractions
|
||||
|
||||
### 1. Core Infrastructure Abstractions
|
||||
|
||||
#### A. `useAsyncOperation` Composable
|
||||
**Location**: `src/core/composables/useAsyncOperation.ts`
|
||||
|
||||
```typescript
|
||||
export function useAsyncOperation<T>() {
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const data = ref<T | null>(null)
|
||||
|
||||
const execute = async (operation: () => Promise<T>, options?: {
|
||||
successMessage?: string
|
||||
errorMessage?: string
|
||||
showToast?: boolean
|
||||
}) => {
|
||||
// Standardized loading/error/success handling
|
||||
}
|
||||
|
||||
return { isLoading, error, data, execute, reset }
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- ✅ Eliminates 200+ lines of duplicate code
|
||||
- ✅ Consistent error handling across all modules
|
||||
- ✅ Standardized loading states
|
||||
- ✅ Optional toast notification integration
|
||||
|
||||
#### B. `BaseService` Abstract Class
|
||||
**Location**: `src/core/base/BaseService.ts`
|
||||
|
||||
```typescript
|
||||
export abstract class BaseService {
|
||||
protected relayHub: any
|
||||
protected authService: any
|
||||
|
||||
constructor() {
|
||||
// Proper dependency injection with type safety
|
||||
}
|
||||
|
||||
protected async initialize(): Promise<void> {
|
||||
await this.waitForDependencies()
|
||||
await this.onInitialize()
|
||||
}
|
||||
|
||||
protected requireAuth() {
|
||||
// Centralized auth requirement checking
|
||||
}
|
||||
|
||||
protected handleError(error: Error, context: string) {
|
||||
// Standardized error handling and reporting
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- ✅ Eliminates `as any` type casting
|
||||
- ✅ Consistent dependency management
|
||||
- ✅ Standardized service lifecycle
|
||||
- ✅ Better error handling and debugging
|
||||
|
||||
#### C. `PaymentService` Centralization
|
||||
**Location**: `src/core/services/PaymentService.ts`
|
||||
|
||||
```typescript
|
||||
export class PaymentService extends BaseService {
|
||||
async processLightningPayment(bolt11: string, orderId?: string): Promise<PaymentResult>
|
||||
async generateQRCode(paymentRequest: string): Promise<string>
|
||||
async monitorPaymentStatus(paymentHash: string): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- ✅ Eliminates 150+ lines of duplicate payment logic
|
||||
- ✅ Consistent payment UX across market and events
|
||||
- ✅ Centralized payment monitoring and error handling
|
||||
- ✅ Single source of truth for Lightning integration
|
||||
|
||||
#### D. `StorageService` Abstraction
|
||||
**Location**: `src/core/services/StorageService.ts`
|
||||
|
||||
```typescript
|
||||
export class StorageService {
|
||||
setUserData<T>(key: string, data: T): void
|
||||
getUserData<T>(key: string, defaultValue?: T): T | undefined
|
||||
clearUserData(key: string): void
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- ✅ User-scoped storage abstraction
|
||||
- ✅ Type-safe storage operations
|
||||
- ✅ Consistent data isolation between users
|
||||
|
||||
### 2. UI/UX Standardization
|
||||
|
||||
#### A. Notification Service
|
||||
**Location**: `src/core/services/NotificationService.ts`
|
||||
|
||||
```typescript
|
||||
export class NotificationService {
|
||||
success(message: string, options?: NotificationOptions): void
|
||||
error(message: string, options?: NotificationOptions): void
|
||||
info(message: string, options?: NotificationOptions): void
|
||||
}
|
||||
```
|
||||
|
||||
#### B. Module Registration Utilities
|
||||
**Location**: `src/core/utils/moduleRegistration.ts`
|
||||
|
||||
```typescript
|
||||
export function createModulePlugin(config: ModuleConfig): ModulePlugin {
|
||||
// Standardized module registration with error handling
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Phase 1: High-Impact Core Abstractions (Week 1-2)
|
||||
1. **Create `useAsyncOperation` composable**
|
||||
- Implement core functionality
|
||||
- Replace usage in 12+ files
|
||||
- **Expected Impact**: 40% reduction in loading/error code
|
||||
|
||||
2. **Create `BaseService` abstract class**
|
||||
- Implement dependency injection abstraction
|
||||
- Migrate 7+ services to inherit from BaseService
|
||||
- **Expected Impact**: Eliminate `as any` casting, improve type safety
|
||||
|
||||
### Phase 2: Service Consolidation (Week 3-4)
|
||||
3. **Create `PaymentService` centralization**
|
||||
- Consolidate market and events payment logic
|
||||
- Implement centralized QR code generation
|
||||
- **Expected Impact**: 30% reduction in payment-related code
|
||||
|
||||
4. **Create `StorageService` abstraction**
|
||||
- Implement user-scoped storage
|
||||
- Migrate existing storage patterns
|
||||
- **Expected Impact**: Consistent data isolation
|
||||
|
||||
### Phase 3: UX Standardization (Week 5-6)
|
||||
5. **Create `NotificationService`**
|
||||
- Standardize toast notifications
|
||||
- Implement consistent messaging
|
||||
- **Expected Impact**: Unified notification experience
|
||||
|
||||
6. **Create module registration utilities**
|
||||
- Simplify module creation process
|
||||
- Standardize registration patterns
|
||||
- **Expected Impact**: Faster module development
|
||||
|
||||
---
|
||||
|
||||
## Expected Benefits
|
||||
|
||||
### Development Velocity
|
||||
- **40% faster module development** with reusable patterns
|
||||
- **Reduced onboarding time** for new developers
|
||||
- **Consistent development patterns** across team
|
||||
|
||||
### Code Quality
|
||||
- **30-40% reduction in duplicate code**
|
||||
- **Improved type safety** with proper service injection
|
||||
- **Standardized error handling** across all modules
|
||||
- **Better test coverage** of centralized functionality
|
||||
|
||||
### User Experience
|
||||
- **Consistent loading states** across all features
|
||||
- **Unified error messaging** and handling
|
||||
- **Standardized payment flows** between market and events
|
||||
- **Better performance** through optimized common operations
|
||||
|
||||
### Maintainability
|
||||
- **Single source of truth** for common patterns
|
||||
- **Easier debugging** with centralized error handling
|
||||
- **Propagated improvements** - enhancements benefit all modules
|
||||
- **Reduced technical debt** through consolidation
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
### Low Risk
|
||||
- **Backward compatibility**: New abstractions won't break existing functionality
|
||||
- **Incremental adoption**: Can be implemented module by module
|
||||
- **Fallback options**: Existing patterns remain functional during transition
|
||||
|
||||
### Medium Risk
|
||||
- **TypeScript complexity**: Need careful typing for generic abstractions
|
||||
- **Testing coverage**: Must ensure centralized code is thoroughly tested
|
||||
|
||||
### Mitigation Strategies
|
||||
- **Comprehensive testing** of all abstracted functionality
|
||||
- **Gradual migration** one module at a time
|
||||
- **Maintain existing APIs** during transition period
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Quantitative Metrics
|
||||
- **Lines of Code**: Target 30-40% reduction in common patterns
|
||||
- **Type Safety**: Eliminate all `as any` casting in services
|
||||
- **Test Coverage**: Achieve 90%+ coverage of centralized functionality
|
||||
- **Build Performance**: Maintain sub-6s production build times
|
||||
|
||||
### Qualitative Metrics
|
||||
- **Developer Experience**: Faster module development and onboarding
|
||||
- **Code Consistency**: Uniform patterns across all modules
|
||||
- **Error Handling**: Consistent error reporting and recovery
|
||||
- **User Experience**: Uniform loading states and notifications
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The modular architecture analysis reveals a mature, well-structured codebase with **significant opportunities for optimization**. The identified abstractions will:
|
||||
|
||||
1. **Reduce code duplication** by 30-40% in common patterns
|
||||
2. **Improve type safety** and eliminate `as any` casting
|
||||
3. **Standardize user experience** across all modules
|
||||
4. **Accelerate future development** through reusable patterns
|
||||
|
||||
**Recommendation**: Proceed with **Phase 1 implementation** starting with `useAsyncOperation` and `BaseService` abstractions, which will deliver immediate benefits with minimal risk.
|
||||
|
||||
The proposed changes align with the existing architectural principles while providing substantial improvements in maintainability, developer experience, and code quality.
|
||||
|
||||
---
|
||||
|
||||
*This analysis was conducted on September 5, 2025, based on the current state of the Ario web application modular architecture.*
|
||||
356
docs/01-architecture/relay-hub.md
Normal file
356
docs/01-architecture/relay-hub.md
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
# Central Relay Hub Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
The Ario application uses a centralized relay hub architecture to manage all Nostr relay connections. This design provides a single point of connection management, efficient resource sharing, and consistent relay behavior across all components.
|
||||
|
||||
## Architecture Components
|
||||
|
||||
### 1. Core Relay Hub (`relayHub.ts`)
|
||||
|
||||
The `RelayHub` class is the foundation of the relay management system, built on top of `nostr-tools` SimplePool.
|
||||
|
||||
**Key Features:**
|
||||
- **Singleton Pattern**: Single instance manages all relay connections
|
||||
- **Connection Pooling**: Efficiently manages multiple relay connections
|
||||
- **Automatic Reconnection**: Handles connection failures and reconnection logic
|
||||
- **Health Monitoring**: Regular health checks and status reporting
|
||||
- **Mobile Optimization**: Handles mobile app visibility changes
|
||||
- **Event Emission**: Provides event-driven architecture for status updates
|
||||
|
||||
**Location:** `src/modules/base/nostr/relay-hub.ts` *(moved from legacy lib/nostr location)*
|
||||
|
||||
### 2. Relay Hub Composable (`useRelayHub.ts`)
|
||||
|
||||
A Vue 3 composable that provides a reactive interface to the relay hub.
|
||||
|
||||
**Key Features:**
|
||||
- **Reactive State**: `isConnected`, `connectionStatus`, `relayStatuses`
|
||||
- **Connection Management**: `initialize()`, `connect()`, `disconnect()`
|
||||
- **Subscription Management**: `subscribe()`, `unsubscribe()`
|
||||
- **Event Publishing**: `publishEvent()`, `queryEvents()`
|
||||
- **Health Monitoring**: Connection health percentages and status
|
||||
|
||||
**Location:** `src/composables/useRelayHub.ts`
|
||||
|
||||
### 3. Application Integration (`App.vue`)
|
||||
|
||||
The main application component initializes the relay hub at startup.
|
||||
|
||||
**Key Integration Points:**
|
||||
```typescript
|
||||
// Initialize relay hub
|
||||
const relayHub = useRelayHub()
|
||||
|
||||
onMounted(async () => {
|
||||
// Initialize relay hub
|
||||
try {
|
||||
await relayHub.initialize()
|
||||
console.log('Relay hub initialized successfully')
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize relay hub:', error)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## How Components Plug Into the Relay Hub
|
||||
|
||||
### 1. Direct Integration via `useRelayHub()`
|
||||
|
||||
Components can directly use the relay hub composable:
|
||||
|
||||
```typescript
|
||||
import { useRelayHub } from '@/composables/useRelayHub'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const relayHub = useRelayHub()
|
||||
|
||||
// Access reactive state
|
||||
const isConnected = relayHub.isConnected
|
||||
const connectionStatus = relayHub.connectionStatus
|
||||
|
||||
// Use connection methods
|
||||
const connect = async () => {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
// Subscribe to events
|
||||
const unsubscribe = relayHub.subscribe({
|
||||
id: 'my-subscription',
|
||||
filters: [{ kinds: [1] }], // Text notes
|
||||
onEvent: (event) => {
|
||||
console.log('Received event:', event)
|
||||
}
|
||||
})
|
||||
|
||||
// Clean up on unmount
|
||||
onUnmounted(() => {
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
return {
|
||||
isConnected,
|
||||
connectionStatus,
|
||||
connect
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Indirect Integration via Specialized Composables
|
||||
|
||||
Components can use specialized composables that internally use the relay hub:
|
||||
|
||||
#### Chat Integration (`useNostrChat.ts`)
|
||||
|
||||
The chat system integrates with the relay hub for message handling:
|
||||
|
||||
```typescript
|
||||
// Inside useNostrChat.ts
|
||||
const subscribeToPeer = async (peerPubkey: string) => {
|
||||
// Check connection status
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
// Subscribe to peer messages
|
||||
const unsubscribe = relayHub.subscribe({
|
||||
id: `peer-${peerPubkey}`,
|
||||
filters: [
|
||||
{
|
||||
kinds: [4], // Direct messages
|
||||
'#p': [currentUser.value.pubkey],
|
||||
authors: [peerPubkey]
|
||||
}
|
||||
],
|
||||
onEvent: (event) => handleIncomingMessage(event, peerPubkey)
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}
|
||||
```
|
||||
|
||||
#### Market Integration (`useMarket.ts`)
|
||||
|
||||
The market system can integrate similarly:
|
||||
|
||||
```typescript
|
||||
// Example market integration
|
||||
const subscribeToMarketEvents = async () => {
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
return relayHub.subscribe({
|
||||
id: 'market-events',
|
||||
filters: [
|
||||
{
|
||||
kinds: [30000], // Custom market events
|
||||
'#t': ['market']
|
||||
}
|
||||
],
|
||||
onEvent: (event) => handleMarketEvent(event)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Configuration and Setup
|
||||
|
||||
#### Relay Configuration
|
||||
|
||||
Relays are configured in the application config:
|
||||
|
||||
```typescript
|
||||
// src/lib/config.ts
|
||||
export const config = {
|
||||
nostr: {
|
||||
relays: [
|
||||
'wss://relay.ariege.io',
|
||||
'wss://lnbits.ariege.io/nostrrelay/benac'
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Initialization Order
|
||||
|
||||
1. **App.vue** initializes the relay hub
|
||||
2. **Components** can use the relay hub immediately after initialization
|
||||
3. **Specialized composables** handle their own relay hub integration
|
||||
|
||||
## Benefits of the Central Relay Hub Architecture
|
||||
|
||||
### 1. **Resource Efficiency**
|
||||
- Single connection pool for all relay connections
|
||||
- Shared connection management across components
|
||||
- Reduced memory usage and connection overhead
|
||||
|
||||
### 2. **Consistent Behavior**
|
||||
- Unified connection status across the application
|
||||
- Consistent error handling and reconnection logic
|
||||
- Standardized relay health monitoring
|
||||
|
||||
### 3. **Scalability**
|
||||
- Easy to add new relay endpoints
|
||||
- Centralized configuration management
|
||||
- Simple to extend with new relay features
|
||||
|
||||
### 4. **Maintainability**
|
||||
- Single point of truth for relay logic
|
||||
- Easier debugging and monitoring
|
||||
- Centralized relay-related bug fixes
|
||||
|
||||
## Best Practices for Component Integration
|
||||
|
||||
### 1. **Always Check Connection Status**
|
||||
|
||||
```typescript
|
||||
const myFunction = async () => {
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
// Proceed with relay operations
|
||||
}
|
||||
```
|
||||
|
||||
### 2. **Use Subscription IDs**
|
||||
|
||||
```typescript
|
||||
const subscriptionId = `my-component-${Date.now()}`
|
||||
const unsubscribe = relayHub.subscribe({
|
||||
id: subscriptionId,
|
||||
filters: [...],
|
||||
onEvent: handleEvent
|
||||
})
|
||||
```
|
||||
|
||||
### 3. **Clean Up Subscriptions**
|
||||
|
||||
```typescript
|
||||
onUnmounted(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 4. **Handle Connection Errors**
|
||||
|
||||
```typescript
|
||||
const connect = async () => {
|
||||
try {
|
||||
await relayHub.connect()
|
||||
} catch (error) {
|
||||
console.error('Failed to connect:', error)
|
||||
// Handle error appropriately
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. **Monitor Connection Health**
|
||||
|
||||
```typescript
|
||||
const connectionHealth = computed(() => relayHub.connectionHealth)
|
||||
const isHealthy = computed(() => connectionHealth.value > 50)
|
||||
```
|
||||
|
||||
## Example: Creating a New Relay-Enabled Component
|
||||
|
||||
Here's a complete example of a new component that uses the relay hub:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<div>Connection Status: {{ connectionStatus }}</div>
|
||||
<div>Connected Relays: {{ connectedRelayCount }}/{{ totalRelayCount }}</div>
|
||||
<button @click="subscribeToEvents" :disabled="!isConnected">
|
||||
Subscribe to Events
|
||||
</button>
|
||||
<div v-if="events.length">
|
||||
<h3>Received Events:</h3>
|
||||
<div v-for="event in events" :key="event.id">
|
||||
{{ event.content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRelayHub } from '@/composables/useRelayHub'
|
||||
|
||||
const relayHub = useRelayHub()
|
||||
const events = ref<any[]>([])
|
||||
let unsubscribe: (() => void) | null = null
|
||||
|
||||
const { isConnected, connectionStatus, connectedRelayCount, totalRelayCount } = relayHub
|
||||
|
||||
const subscribeToEvents = async () => {
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
unsubscribe = relayHub.subscribe({
|
||||
id: 'my-component-events',
|
||||
filters: [
|
||||
{
|
||||
kinds: [1], // Text notes
|
||||
limit: 10
|
||||
}
|
||||
],
|
||||
onEvent: (event) => {
|
||||
events.value.push(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Connection Not Established**
|
||||
- Check if relay hub is initialized in App.vue
|
||||
- Verify relay URLs in configuration
|
||||
- Check browser console for connection errors
|
||||
|
||||
2. **Subscriptions Not Working**
|
||||
- Ensure connection is established before subscribing
|
||||
- Verify subscription filters are correct
|
||||
- Check if unsubscribe function is called prematurely
|
||||
|
||||
3. **Performance Issues**
|
||||
- Monitor number of active subscriptions
|
||||
- Use appropriate filter limits
|
||||
- Clean up unused subscriptions
|
||||
|
||||
### Debug Tools
|
||||
|
||||
The relay hub provides several debugging capabilities:
|
||||
|
||||
```typescript
|
||||
// Check connection status
|
||||
console.log('Connected:', relayHub.isConnected.value)
|
||||
console.log('Relay count:', relayHub.totalRelayCount)
|
||||
|
||||
// Monitor relay health
|
||||
console.log('Health:', relayHub.connectionHealth.value)
|
||||
|
||||
// Check specific relay status
|
||||
const status = relayHub.getRelayStatus('wss://relay.example.com')
|
||||
console.log('Relay status:', status)
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
The central relay hub architecture provides a robust, efficient, and maintainable way to manage Nostr relay connections across the Ario application. By following the integration patterns outlined in this document, components can easily leverage the relay hub's capabilities while maintaining clean separation of concerns and optimal performance.
|
||||
|
||||
For questions or issues with relay hub integration, refer to the existing implementations in `useNostrChat.ts` and `useMarket.ts` as reference examples.
|
||||
Loading…
Add table
Add a link
Reference in a new issue