Update modular design documentation and workspace configuration

- Change the active file in workspace.json to point to the new modular-design.md.
- Revise modular-design.md to enhance clarity and structure, including a new table of contents and updated sections on design philosophy, architecture components, and service abstractions.
- Remove outdated content and improve the overall presentation of modular design patterns and best practices.

These updates aim to streamline the documentation for better accessibility and understanding of the modular architecture.
This commit is contained in:
padreug 2025-09-08 12:03:28 +02:00
parent 670a42dd9b
commit a373fa714d
5 changed files with 620 additions and 515 deletions

View file

@ -1,419 +1,391 @@
# Modular Architecture Analysis & Infrastructure Abstractions
# 🔧 Modular Design Patterns
**Project**: Ario Web Application
**Date**: September 5, 2025
**Scope**: Analysis of modular plugin architecture for centralization opportunities
> **Plugin-based architecture** enabling scalable, maintainable, and extensible feature development with dependency injection, service abstractions, and clean modular boundaries.
## Table of Contents
- [[#Design Philosophy]]
- [[#Architecture Components]]
- [[#Service Abstractions]]
- [[#Module Development]]
- [[#Implementation Patterns]]
- [[#Benefits & Trade-offs]]
## Design Philosophy
### **Separation of Concerns**
Each module owns its complete domain logic, from data models to UI components, ensuring clear boundaries and reducing coupling between features.
### **Dependency Injection**
Services communicate through well-defined interfaces using a centralized DI container, enabling loose coupling and testability.
### **Plugin-Based Development**
Features are implemented as self-contained modules that can be independently developed, tested, and deployed.
### **Reactive Architecture**
Services integrate Vue's reactivity system to provide automatic UI updates and consistent state management.
---
## Executive Summary
## Architecture Components
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.
### **Plugin Manager**
Orchestrates module loading, dependency resolution, and lifecycle management:
**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
class PluginManager {
async loadModules(modules: ModulePlugin[]): Promise<void>
getDependencyGraph(): Map<string, string[]>
validateDependencies(modules: ModulePlugin[]): void
}
```
**Impact**:
- 200+ lines of duplicate code
- Inconsistent error handling
- Manual loading state management
### **Dependency Injection Container**
Manages service registration, injection, and lifecycle:
### 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
// Service registration
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
if (!relayHub) {
throw new Error('RelayHub not available')
// Service consumption
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
```
### **BaseService Architecture**
Abstract foundation providing common patterns for all services:
```typescript
abstract class BaseService {
protected readonly metadata: ServiceMetadata
protected dependencies = new Map<string, any>()
abstract initialize(): Promise<void>
abstract dispose(): Promise<void>
}
```
**Impact**:
- Type safety issues (`as any` casting)
- Duplicate dependency checking
- Inconsistent error messages
### **Module Plugin Interface**
Standardized contract for all feature modules:
### 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
interface ModulePlugin {
name: string
version: string
dependencies: string[]
install(app: App, options?: ModuleConfig): Promise<void>
routes?: RouteRecordRaw[]
components?: Record<string, Component>
}
```
**Impact**:
- 150+ lines of duplicate payment logic
- Inconsistent payment UX
- Duplicate error handling
## Service Abstractions
### 4. User-Scoped Storage Pattern
### **Core Infrastructure Services**
**Occurrences**: Found in market store, chat service
#### **AuthService** - User Identity Management
- **Purpose**: Centralized authentication with Nostr key handling
- **Features**: Session management, profile updates, secure key storage
- **Pattern**: BaseService with reactive state management
**Current Implementation**:
#### **RelayHub** - Nostr Connection Management
- **Purpose**: Centralized relay connections and event processing
- **Features**: Connection pooling, automatic reconnection, event deduplication
- **Pattern**: Service singleton with connection state management
#### **InvoiceService** - Lightning Payment Processing
- **Purpose**: Unified Lightning Network integration
- **Features**: Invoice creation, payment monitoring, QR code generation
- **Pattern**: BaseService with LNBits API integration
#### **ToastService** - User Notifications
- **Purpose**: Consistent notification system across modules
- **Features**: Context-specific messages, accessibility support
- **Pattern**: Service with method categorization by context
#### **StorageService** - User-Scoped Data Persistence
- **Purpose**: Secure, user-isolated local storage operations
- **Features**: Encryption, type safety, reactive storage
- **Pattern**: Service with automatic user prefixing
### **Shared Composables**
#### **useAuth()** - Authentication Access
```typescript
// Pattern repeated across modules
const getUserStorageKey = (baseKey: string) => {
const userPubkey = auth.currentUser?.value?.pubkey
return userPubkey ? `${baseKey}_${userPubkey}` : baseKey
}
const auth = useAuth()
const { isAuthenticated, currentUser, login, logout } = auth
```
**Impact**:
- Duplicate storage key logic
- Potential data consistency issues
### 5. Module Registration Pattern
**Occurrences**: Every module (`index.ts` files)
**Current Implementation**:
#### **useToast()** - Notification Access
```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!')
const toast = useToast()
toast.success('Operation completed!')
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`
#### **useStorage()** - Storage Access
```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 }
}
const storage = useStorage()
const userData = storage.getUserData('preferences', defaultPrefs)
```
**Benefits**:
- ✅ Eliminates 200+ lines of duplicate code
- ✅ Consistent error handling across all modules
- ✅ Standardized loading states
- ✅ Optional toast notification integration
## Module Development
#### B. `BaseService` Abstract Class
**Location**: `src/core/base/BaseService.ts`
### **Module Structure Pattern**
Each module follows a consistent directory structure:
```
src/modules/[module-name]/
├── index.ts # Module plugin definition
├── components/ # Module-specific UI components
├── composables/ # Module composables and hooks
├── services/ # Business logic services
├── stores/ # Module-specific Pinia stores
├── types/ # TypeScript type definitions
└── views/ # Page components and routes
```
### **Service Development Pattern**
Services extend BaseService for consistent lifecycle management:
```typescript
export abstract class BaseService {
protected relayHub: any
protected authService: any
export class MyService extends BaseService {
protected readonly metadata = {
name: 'MyService',
dependencies: ['AuthService', 'RelayHub']
}
constructor() {
// Proper dependency injection with type safety
super()
}
protected async initialize(): Promise<void> {
await this.waitForDependencies()
await this.onInitialize()
async initialize(): Promise<void> {
await super.initialize() // Initialize dependencies
// Service-specific initialization
this.isInitialized.value = true
}
protected requireAuth() {
// Centralized auth requirement checking
async dispose(): Promise<void> {
// Cleanup logic
this.isDisposed.value = true
}
}
```
### **Module Registration Pattern**
Standardized module plugin structure:
```typescript
export const myModule: ModulePlugin = {
name: 'my-module',
version: '1.0.0',
dependencies: ['base'],
async install(app: App, options?: MyModuleConfig) {
// 1. Create and register services
const myService = new MyService()
container.provide(SERVICE_TOKENS.MY_SERVICE, myService)
// 2. Register global components
app.component('MyGlobalComponent', MyGlobalComponent)
// 3. Initialize services
await myService.initialize()
},
routes: [
{
path: '/my-feature',
component: () => import('./views/MyFeatureView.vue')
}
]
}
```
### **Dependency Declaration**
Services declare dependencies through metadata:
```typescript
protected readonly metadata = {
name: 'ChatService',
dependencies: ['AuthService', 'RelayHub', 'StorageService']
}
// Access injected dependencies
private get authService(): AuthService {
return this.dependencies.get('AuthService') as AuthService
}
```
## Implementation Patterns
### **Cross-Module Communication**
#### **Service Dependencies**
For required functionality between modules:
```typescript
// ✅ Correct: Use dependency injection
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
// ❌ Wrong: Direct import breaks modularity
import { relayHub } from '../base/services/relay-hub'
```
#### **Event Bus Communication**
For optional cross-module notifications:
```typescript
// Publishing events
eventBus.emit('user:authenticated', { userId: user.pubkey })
eventBus.emit('payment:received', { amount: 1000, invoiceId: 'abc123' })
// Subscribing to events
eventBus.on('chat:message-received', (message) => {
// Handle cross-module events
})
```
#### **Shared Components**
Modules can export components for reuse:
```typescript
// In module plugin definition
export const chatModule: ModulePlugin = {
components: {
'ChatAvatar': () => import('./components/ChatAvatar.vue'),
'MessageBubble': () => import('./components/MessageBubble.vue')
}
}
// Usage in other modules
<ChatAvatar :pubkey="user.pubkey" :size="32" />
```
### **State Management Patterns**
#### **Service-Based State**
Core state managed by services:
```typescript
// AuthService manages authentication state
export class AuthService extends BaseService {
public isAuthenticated = ref(false)
public user = ref<User | null>(null)
// Computed properties for components
public userDisplay = computed(() => this.user.value?.name || 'Anonymous')
}
```
#### **Module-Specific Stores**
Complex state handled by Pinia stores:
```typescript
// Market module store
export const useMarketStore = defineStore('market', () => {
const products = ref<Product[]>([])
const cartItems = ref<CartItem[]>([])
// Access shared services
const auth = useAuth()
const toast = useToast()
return { products, cartItems }
})
```
### **Error Handling Patterns**
#### **Service-Level Error Handling**
Centralized error handling in services:
```typescript
export class MyService extends BaseService {
protected handleError(error: Error, context: string) {
// Standardized error handling and reporting
console.error(`[${this.metadata.name}] ${context}:`, error)
// Use toast service for user notifications
const toast = injectService(SERVICE_TOKENS.TOAST_SERVICE)
toast.error(`${context} failed: ${error.message}`)
}
}
```
**Benefits**:
- ✅ Eliminates `as any` type casting
- ✅ Consistent dependency management
- ✅ Standardized service lifecycle
- ✅ Better error handling and debugging
#### **Component-Level Error Handling**
Consistent error display in components:
```vue
<script setup lang="ts">
const { error, isLoading, execute } = useAsyncOperation()
#### C. `PaymentService` Centralization
**Location**: `src/core/services/PaymentService.ts`
const handleAction = () => execute(async () => {
await myService.performAction()
})
</script>
```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>
}
<template>
<div>
<button @click="handleAction" :disabled="isLoading">
{{ isLoading ? 'Loading...' : 'Perform Action' }}
</button>
<div v-if="error" class="error">{{ error }}</div>
</div>
</template>
```
**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
## Benefits & Trade-offs
#### D. `StorageService` Abstraction
**Location**: `src/core/services/StorageService.ts`
### **Achieved Benefits**
```typescript
export class StorageService {
setUserData<T>(key: string, data: T): void
getUserData<T>(key: string, defaultValue?: T): T | undefined
clearUserData(key: string): void
}
```
#### **Development Velocity**
- **Consistent Patterns** - Standardized development approaches across modules
- **Reusable Services** - Shared infrastructure reduces implementation time
- **Type Safety** - Dependency injection eliminates `as any` casting
- **Clear Boundaries** - Module separation simplifies feature development
**Benefits**:
- ✅ User-scoped storage abstraction
- ✅ Type-safe storage operations
- ✅ Consistent data isolation between users
#### **Maintainability**
- **Single Source of Truth** - Centralized services eliminate duplication
- **Dependency Visibility** - Clear service relationships and requirements
- **Testability** - Isolated modules and mockable dependencies
- **Extensibility** - Easy to add new modules without affecting existing ones
### 2. UI/UX Standardization
#### **User Experience**
- **Consistent Interface** - Unified patterns across all features
- **Reliable Performance** - Optimized shared services
- **Progressive Loading** - Lazy-loaded modules for faster initial load
- **Error Recovery** - Consistent error handling and user feedback
#### A. Notification Service
**Location**: `src/core/services/NotificationService.ts`
### **Architectural Trade-offs**
```typescript
export class NotificationService {
success(message: string, options?: NotificationOptions): void
error(message: string, options?: NotificationOptions): void
info(message: string, options?: NotificationOptions): void
}
```
#### **Complexity vs. Flexibility**
- **Increased Initial Setup** - More boilerplate for dependency injection
- **Enhanced Flexibility** - Easy to swap implementations and add features
- **Learning Curve** - Developers need to understand DI patterns
- **Long-term Benefits** - Reduced complexity as application grows
#### B. Module Registration Utilities
**Location**: `src/core/utils/moduleRegistration.ts`
#### **Performance Considerations**
- **Service Initialization** - Dependency resolution overhead at startup
- **Memory Efficiency** - Singleton services reduce memory usage
- **Bundle Optimization** - Module-based code splitting improves loading
- **Runtime Performance** - Optimized shared services benefit all modules
```typescript
export function createModulePlugin(config: ModuleConfig): ModulePlugin {
// Standardized module registration with error handling
}
```
### **Best Practices Established**
1. **Always use dependency injection** for cross-module service access
2. **Extend BaseService** for all business logic services
3. **Declare dependencies explicitly** in service metadata
4. **Use reactive state** for UI-affecting service properties
5. **Handle errors consistently** through standardized patterns
6. **Test modules in isolation** with mocked dependencies
## See Also
### Architecture Documentation
- **[[index|🏗️ Architecture Overview]]** - System design principles
- **[[dependency-injection|⚙️ Dependency Injection]]** - DI container system
- **[[authentication-architecture|🔐 Authentication Architecture]]** - Auth service patterns
### Development Guides
- **[[../04-development/index|💻 Development Guide]]** - Module development workflows
- **[[../02-modules/index|📦 Module System]]** - Individual module documentation
- **[[../03-core-services/index|⚙️ Core Services]]** - Infrastructure service details
---
## 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.*
**Tags:** #architecture #modular-design #dependency-injection #plugin-system
**Last Updated:** 2025-09-07
**Author:** Development Team