- 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>
496 lines
No EOL
15 KiB
Markdown
496 lines
No EOL
15 KiB
Markdown
# ⚙️ Core Services Overview
|
|
|
|
> **Shared infrastructure services** providing foundational functionality across all modules with reactive architecture, dependency injection, and lifecycle management.
|
|
|
|
## Table of Contents
|
|
|
|
- [[#Service Architecture]]
|
|
- [[#Available Services]]
|
|
- [[#Service Lifecycle]]
|
|
- [[#Dependency Injection]]
|
|
- [[#Service Development]]
|
|
- [[#Testing Services]]
|
|
|
|
## Service Architecture
|
|
|
|
### **BaseService Foundation**
|
|
All core services extend the `BaseService` abstract class which provides:
|
|
|
|
- **Reactive State Management** - Integration with Vue's reactivity system
|
|
- **Lifecycle Management** - Standardized initialization and disposal
|
|
- **Error Handling** - Consistent error patterns across services
|
|
- **Type Safety** - Full TypeScript support with strict typing
|
|
|
|
```typescript
|
|
abstract class BaseService {
|
|
protected isInitialized = ref(false)
|
|
protected isDisposed = ref(false)
|
|
|
|
abstract initialize(): Promise<void>
|
|
abstract dispose(): Promise<void>
|
|
|
|
// Reactive state helpers
|
|
protected createReactiveState<T>(initialValue: T): Ref<T>
|
|
protected createComputedState<T>(getter: () => T): ComputedRef<T>
|
|
}
|
|
```
|
|
|
|
### **Service Registration Pattern**
|
|
Services are registered in the dependency injection container during module installation:
|
|
|
|
```typescript
|
|
// Service registration (in base module)
|
|
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
|
|
container.provide(SERVICE_TOKENS.AUTH_SERVICE, authService)
|
|
container.provide(SERVICE_TOKENS.STORAGE_SERVICE, storageService)
|
|
|
|
// Service consumption (anywhere in app)
|
|
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
|
|
const auth = injectService(SERVICE_TOKENS.AUTH_SERVICE)
|
|
```
|
|
|
|
## Available Services
|
|
|
|
### **AuthService** 🔐
|
|
**Purpose:** User authentication and identity management
|
|
**Location:** `src/modules/base/auth/auth-service.ts`
|
|
**Token:** `SERVICE_TOKENS.AUTH_SERVICE`
|
|
|
|
**Key Features:**
|
|
- **Key Management** - Secure generation, import, and storage of Nostr keys
|
|
- **User Sessions** - Persistent authentication with encrypted storage
|
|
- **Profile Management** - User profile creation and updates
|
|
- **Security** - Client-side key handling with no server storage
|
|
|
|
**Reactive State:**
|
|
```typescript
|
|
interface AuthService {
|
|
user: Ref<NostrUser | null> // Current authenticated user
|
|
isAuthenticated: ComputedRef<boolean> // Authentication status
|
|
isLoading: Ref<boolean> // Loading state
|
|
loginError: Ref<string | null> // Login error message
|
|
}
|
|
```
|
|
|
|
**Key Methods:**
|
|
- `generateKeyPair()` - Create new Nostr key pair
|
|
- `loginWithPrivateKey(privateKey: string)` - Authenticate with existing key
|
|
- `logout()` - Clear session and user data
|
|
- `updateProfile(profile: UserMetadata)` - Update user profile
|
|
|
|
**See:** [[authentication|📖 Authentication Service Documentation]]
|
|
|
|
### **RelayHub** 🌐
|
|
**Purpose:** Centralized Nostr relay connection management
|
|
**Location:** `src/modules/base/nostr/relay-hub.ts`
|
|
**Token:** `SERVICE_TOKENS.RELAY_HUB`
|
|
|
|
**Key Features:**
|
|
- **Connection Management** - Automatic connection, reconnection, and failover
|
|
- **Event Publishing** - Reliable event publishing across multiple relays
|
|
- **Subscription Management** - Efficient event subscriptions with deduplication
|
|
- **Performance Monitoring** - Relay latency and success rate tracking
|
|
|
|
**Reactive State:**
|
|
```typescript
|
|
interface RelayHub {
|
|
connectedRelays: Ref<string[]> // Currently connected relays
|
|
connectionStatus: ComputedRef<ConnectionStatus> // Overall connection status
|
|
relayStats: Ref<Map<string, RelayStats>> // Per-relay statistics
|
|
isConnecting: Ref<boolean> // Connection in progress
|
|
}
|
|
```
|
|
|
|
**Key Methods:**
|
|
- `connect(relays: string[])` - Connect to relay URLs
|
|
- `publishEvent(event: NostrEvent)` - Publish event to all connected relays
|
|
- `subscribe(filters: Filter[], callback: EventCallback)` - Subscribe to events
|
|
- `getRelayInfo(url: string)` - Get relay connection information
|
|
|
|
**See:** [[../01-architecture/relay-hub|📖 Relay Hub Architecture Documentation]]
|
|
|
|
### **StorageService** 💾
|
|
**Purpose:** User-scoped local storage operations
|
|
**Location:** `src/core/services/StorageService.ts`
|
|
**Token:** `SERVICE_TOKENS.STORAGE_SERVICE`
|
|
|
|
**Key Features:**
|
|
- **User-Scoped Storage** - Automatic key prefixing per authenticated user
|
|
- **Type-Safe Operations** - Strongly typed get/set operations with JSON serialization
|
|
- **Reactive Updates** - Optional reactive storage with Vue refs
|
|
- **Migration Support** - Data migration between storage schema versions
|
|
|
|
**Key Methods:**
|
|
```typescript
|
|
interface StorageService {
|
|
setUserData<T>(key: string, data: T): void
|
|
getUserData<T>(key: string, defaultValue?: T): T | undefined
|
|
removeUserData(key: string): void
|
|
clearUserData(): void
|
|
|
|
// Reactive variants
|
|
getReactiveUserData<T>(key: string, defaultValue: T): Ref<T>
|
|
setReactiveUserData<T>(key: string, ref: Ref<T>): void
|
|
}
|
|
```
|
|
|
|
**Storage Patterns:**
|
|
- User-specific keys: `user:{pubkey}:settings`
|
|
- Global application keys: `app:theme`
|
|
- Module-specific keys: `user:{pubkey}:chat:contacts`
|
|
|
|
**See:** [[storage-service|📖 Storage Service Documentation]]
|
|
|
|
### **ToastService** 📢
|
|
**Purpose:** Application-wide notifications and user feedback
|
|
**Location:** `src/core/services/ToastService.ts`
|
|
**Token:** `SERVICE_TOKENS.TOAST_SERVICE`
|
|
|
|
**Key Features:**
|
|
- **Context-Specific Methods** - Pre-configured toasts for common scenarios
|
|
- **Consistent Messaging** - Standardized success, error, and info messages
|
|
- **Accessibility** - Screen reader compatible notifications
|
|
- **Customizable** - Support for custom toast content and actions
|
|
|
|
**Organized by Context:**
|
|
```typescript
|
|
interface ToastService {
|
|
// Authentication context
|
|
auth: {
|
|
loginSuccess(): void
|
|
loginError(error?: string): void
|
|
logoutSuccess(): void
|
|
keyGenerated(): void
|
|
}
|
|
|
|
// Payment context
|
|
payment: {
|
|
invoiceCreated(): void
|
|
paymentReceived(): void
|
|
paymentFailed(error?: string): void
|
|
}
|
|
|
|
// Clipboard operations
|
|
clipboard: {
|
|
copied(item?: string): void
|
|
copyFailed(): void
|
|
}
|
|
|
|
// General operations
|
|
operation: {
|
|
success(message: string): void
|
|
error(message: string): void
|
|
info(message: string): void
|
|
}
|
|
}
|
|
```
|
|
|
|
**See:** [[toast-service|📖 Toast Service Documentation]]
|
|
|
|
### **EventBus** 📡
|
|
**Purpose:** Inter-module communication and event coordination
|
|
**Location:** `src/core/services/EventBus.ts`
|
|
**Token:** `SERVICE_TOKENS.EVENT_BUS`
|
|
|
|
**Key Features:**
|
|
- **Type-Safe Events** - Strongly typed event payloads
|
|
- **Module Isolation** - Clean communication between modules
|
|
- **Event Namespacing** - Organized event names by domain
|
|
- **Subscription Management** - Easy subscribe/unsubscribe patterns
|
|
|
|
**Event Categories:**
|
|
```typescript
|
|
interface EventBusEvents {
|
|
// User events
|
|
'user:authenticated': { userId: string, profile: UserMetadata }
|
|
'user:profile-updated': { userId: string, changes: Partial<UserMetadata> }
|
|
'user:logout': { userId: string }
|
|
|
|
// Chat events
|
|
'chat:message-received': { messageId: string, from: string, content: string }
|
|
'chat:typing-start': { from: string, chatId: string }
|
|
|
|
// Payment events
|
|
'payment:invoice-created': { invoiceId: string, amount: number }
|
|
'payment:received': { invoiceId: string, amount: number }
|
|
|
|
// Relay events
|
|
'relay:connected': { url: string }
|
|
'relay:disconnected': { url: string, reason?: string }
|
|
}
|
|
```
|
|
|
|
**See:** [[../01-architecture/event-bus|📖 Event Bus Communication Documentation]]
|
|
|
|
## Service Lifecycle
|
|
|
|
### **Initialization Phase**
|
|
Services are initialized in dependency order during application startup:
|
|
|
|
```typescript
|
|
// 1. Base services (no dependencies)
|
|
await authService.initialize()
|
|
await storageService.initialize()
|
|
|
|
// 2. Infrastructure services (depend on base services)
|
|
await relayHub.initialize()
|
|
await toastService.initialize()
|
|
|
|
// 3. Feature services (depend on infrastructure)
|
|
await chatService.initialize()
|
|
await eventsService.initialize()
|
|
```
|
|
|
|
### **Service Dependencies**
|
|
Services declare their dependencies through constructor injection:
|
|
|
|
```typescript
|
|
class ChatService extends BaseService {
|
|
constructor(
|
|
private auth = injectService(SERVICE_TOKENS.AUTH_SERVICE),
|
|
private relayHub = injectService(SERVICE_TOKENS.RELAY_HUB),
|
|
private storage = injectService(SERVICE_TOKENS.STORAGE_SERVICE),
|
|
private eventBus = injectService(SERVICE_TOKENS.EVENT_BUS)
|
|
) {
|
|
super()
|
|
}
|
|
}
|
|
```
|
|
|
|
### **Disposal Phase**
|
|
Services are disposed in reverse dependency order during application shutdown:
|
|
|
|
```typescript
|
|
async dispose(): Promise<void> {
|
|
// Clean up subscriptions
|
|
this.subscriptions.forEach(sub => sub.close())
|
|
|
|
// Save persistent state
|
|
await this.storage.setUserData('chat:messages', this.messages.value)
|
|
|
|
// Mark as disposed
|
|
this.isDisposed.value = true
|
|
}
|
|
```
|
|
|
|
## Dependency Injection
|
|
|
|
### **Service Tokens**
|
|
Type-safe service tokens prevent runtime errors and enable proper TypeScript inference:
|
|
|
|
```typescript
|
|
export const SERVICE_TOKENS = {
|
|
AUTH_SERVICE: Symbol('AUTH_SERVICE') as InjectionKey<AuthService>,
|
|
RELAY_HUB: Symbol('RELAY_HUB') as InjectionKey<RelayHub>,
|
|
STORAGE_SERVICE: Symbol('STORAGE_SERVICE') as InjectionKey<StorageService>,
|
|
TOAST_SERVICE: Symbol('TOAST_SERVICE') as InjectionKey<ToastService>,
|
|
} as const
|
|
```
|
|
|
|
### **Service Registration**
|
|
Services are registered during module installation:
|
|
|
|
```typescript
|
|
// In base module installation
|
|
export async function installBaseModule(app: App) {
|
|
// Create service instances
|
|
const authService = new AuthService()
|
|
const relayHub = new RelayHub()
|
|
const storageService = new StorageService()
|
|
|
|
// Register in container
|
|
container.provide(SERVICE_TOKENS.AUTH_SERVICE, authService)
|
|
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
|
|
container.provide(SERVICE_TOKENS.STORAGE_SERVICE, storageService)
|
|
|
|
// Initialize services
|
|
await authService.initialize()
|
|
await relayHub.initialize()
|
|
}
|
|
```
|
|
|
|
### **Service Consumption**
|
|
Services are injected where needed using type-safe injection:
|
|
|
|
```typescript
|
|
// In composables
|
|
export function useAuth() {
|
|
const authService = injectService(SERVICE_TOKENS.AUTH_SERVICE)
|
|
|
|
return {
|
|
user: authService.user,
|
|
login: authService.login,
|
|
logout: authService.logout
|
|
}
|
|
}
|
|
|
|
// In components
|
|
<script setup>
|
|
const toast = injectService(SERVICE_TOKENS.TOAST_SERVICE)
|
|
const handleSuccess = () => toast.operation.success('Action completed!')
|
|
</script>
|
|
```
|
|
|
|
## Service Development
|
|
|
|
### **Creating a New Service**
|
|
|
|
#### 1. Service Class Implementation
|
|
```typescript
|
|
// src/core/services/MyService.ts
|
|
export class MyService extends BaseService {
|
|
// Reactive state
|
|
private readonly _data = ref<MyData[]>([])
|
|
private readonly _isLoading = ref(false)
|
|
|
|
// Public readonly access to state
|
|
public readonly data = readonly(this._data)
|
|
public readonly isLoading = readonly(this._isLoading)
|
|
|
|
constructor(
|
|
private dependency = injectService(SERVICE_TOKENS.DEPENDENCY)
|
|
) {
|
|
super()
|
|
}
|
|
|
|
async initialize(): Promise<void> {
|
|
// Initialization logic
|
|
await this.loadInitialData()
|
|
this.isInitialized.value = true
|
|
}
|
|
|
|
async dispose(): Promise<void> {
|
|
// Cleanup logic
|
|
this._data.value = []
|
|
this.isDisposed.value = true
|
|
}
|
|
|
|
// Public methods
|
|
async createItem(item: CreateItemRequest): Promise<MyData> {
|
|
this._isLoading.value = true
|
|
try {
|
|
const newItem = await this.dependency.create(item)
|
|
this._data.value.push(newItem)
|
|
return newItem
|
|
} finally {
|
|
this._isLoading.value = false
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2. Service Token Registration
|
|
```typescript
|
|
// Add to SERVICE_TOKENS
|
|
export const SERVICE_TOKENS = {
|
|
// ... existing tokens
|
|
MY_SERVICE: Symbol('MY_SERVICE') as InjectionKey<MyService>,
|
|
} as const
|
|
```
|
|
|
|
#### 3. Service Registration in Module
|
|
```typescript
|
|
// In module installation
|
|
const myService = new MyService()
|
|
container.provide(SERVICE_TOKENS.MY_SERVICE, myService)
|
|
await myService.initialize()
|
|
```
|
|
|
|
### **Service Best Practices**
|
|
|
|
#### **Reactive State Management**
|
|
- Use `ref()` for mutable state, `readonly()` for public access
|
|
- Provide computed properties for derived state
|
|
- Use `watch()` and `watchEffect()` for side effects
|
|
|
|
#### **Error Handling**
|
|
- Throw descriptive errors with proper types
|
|
- Use try/catch blocks with proper cleanup
|
|
- Log errors appropriately for debugging
|
|
|
|
#### **Performance Optimization**
|
|
- Implement proper subscription cleanup in `dispose()`
|
|
- Use debouncing for frequent operations
|
|
- Cache expensive computations with `computed()`
|
|
|
|
## Testing Services
|
|
|
|
### **Service Unit Tests**
|
|
```typescript
|
|
// tests/unit/services/MyService.test.ts
|
|
describe('MyService', () => {
|
|
let service: MyService
|
|
let mockDependency: MockType<DependencyService>
|
|
|
|
beforeEach(() => {
|
|
// Setup mocks
|
|
mockDependency = createMockService()
|
|
|
|
// Create service with mocks
|
|
service = new MyService(mockDependency)
|
|
})
|
|
|
|
afterEach(async () => {
|
|
await service.dispose()
|
|
})
|
|
|
|
it('should initialize correctly', async () => {
|
|
await service.initialize()
|
|
|
|
expect(service.isInitialized.value).toBe(true)
|
|
expect(service.data.value).toEqual([])
|
|
})
|
|
|
|
it('should create items', async () => {
|
|
await service.initialize()
|
|
|
|
const item = await service.createItem({ name: 'test' })
|
|
|
|
expect(service.data.value).toContain(item)
|
|
expect(mockDependency.create).toHaveBeenCalledWith({ name: 'test' })
|
|
})
|
|
})
|
|
```
|
|
|
|
### **Integration Tests**
|
|
```typescript
|
|
// tests/integration/services/ServiceIntegration.test.ts
|
|
describe('Service Integration', () => {
|
|
let container: DIContainer
|
|
|
|
beforeEach(async () => {
|
|
container = createTestContainer()
|
|
await installTestServices(container)
|
|
})
|
|
|
|
it('should handle cross-service communication', async () => {
|
|
const authService = container.get(SERVICE_TOKENS.AUTH_SERVICE)
|
|
const chatService = container.get(SERVICE_TOKENS.CHAT_SERVICE)
|
|
|
|
await authService.login('test-key')
|
|
const message = await chatService.sendMessage('Hello')
|
|
|
|
expect(message.author).toBe(authService.user.value?.pubkey)
|
|
})
|
|
})
|
|
```
|
|
|
|
## See Also
|
|
|
|
### Service Documentation
|
|
- **[[authentication|🔐 Authentication Service]]** - User identity and session management
|
|
- **[[storage-service|💾 Storage Service]]** - User-scoped data persistence
|
|
- **[[toast-service|📢 Toast Service]]** - User notifications and feedback
|
|
- **[[visibility-service|👁️ Visibility Service]]** - Dynamic UI component control
|
|
|
|
### Architecture References
|
|
- **[[../01-architecture/dependency-injection|⚙️ Dependency Injection]]** - DI container system
|
|
- **[[../01-architecture/event-bus|📡 Event Bus Communication]]** - Inter-service messaging
|
|
- **[[../02-modules/index|📦 Module System]]** - How services integrate with modules
|
|
- **[[../04-development/testing|🧪 Testing Guide]]** - Service testing patterns
|
|
|
|
---
|
|
|
|
**Tags:** #services #architecture #dependency-injection #reactive-state
|
|
**Last Updated:** 2025-09-06
|
|
**Author:** Development Team |