2025-04-16 22:33:33 +02:00

183 lines
5.9 KiB
Markdown

# System Patterns
This document outlines the system architecture, key technical decisions, and design patterns used in the G1 TypeScript Common Packages repository.
## System Architecture
The repository follows a monorepo architecture using pnpm workspaces:
```
g1-ts-common-packages/
├── packages/ # All packages are stored here
│ ├── sse-client/ # Server-Sent Events client package
│ └── [future-packages]/ # Additional packages will be added here
├── scripts/ # Utility scripts for the repository
├── docs/ # Documentation
└── .github/workflows/ # CI/CD workflows
```
Each package within the monorepo is designed to be:
- **Independent**: Can be used without other packages from the monorepo
- **Focused**: Solves a specific problem domain
- **Well-documented**: Includes comprehensive documentation
- **Well-tested**: Includes unit tests
## Key Technical Decisions
### 1. Monorepo Structure
- **Decision**: Use a monorepo structure with pnpm workspaces
- **Rationale**:
- Simplifies management of multiple related packages
- Enables sharing of configuration and tooling
- Facilitates coordinated changes across packages
- Reduces duplication of dependencies
### 2. Package Scope
- **Decision**: Use the `@g1` scope for all packages
- **Rationale**:
- Provides namespace isolation
- Makes it clear that packages belong to Generation One
- Prevents name collisions with other packages
### 3. Package Versioning
- **Decision**: Start packages at version 0.2.0
- **Rationale**:
- Follows Generation One's standard starting version
- Allows for pre-1.0 breaking changes as packages mature
### 4. Publishing Strategy
- **Decision**: Publish packages to Gitea registry
- **Rationale**:
- Keeps packages within Generation One's infrastructure
- Provides access control for private packages (if needed in the future)
- Simplifies CI/CD integration
### 5. TypeScript First
- **Decision**: Use TypeScript for all packages
- **Rationale**:
- Provides type safety
- Improves developer experience
- Enables better tooling and IDE support
- Reduces runtime errors
## Design Patterns
### SSE Client Package
The SSE Client package demonstrates several design patterns:
#### 1. Singleton Pattern
- Used in the `SSEConnectionManager` to ensure only one instance manages all connections
- Provides a global point of access to the connection manager
```typescript
export class SSEConnectionManager {
private static instance: SSEConnectionManager;
private constructor() {}
public static getInstance(): SSEConnectionManager {
if (!SSEConnectionManager.instance) {
SSEConnectionManager.instance = new SSEConnectionManager();
}
return SSEConnectionManager.instance;
}
// ...
}
```
#### 2. Factory Pattern
- Helper functions like `getSSEConnection` act as factories to create or retrieve SSE client instances
- Simplifies the creation and management of SSE connections
```typescript
export function getSSEConnection(url: string, id: string, options: SSEClientOptions = {}): SSEClient {
return SSEConnectionManager.getInstance().getConnection(url, id, options);
}
```
#### 3. Observer Pattern
- The SSE client implements an event-based system where clients can subscribe to events
- Uses the browser's `EventTarget` or a custom implementation in Node.js
```typescript
public on(eventName: string, listener: (event: any) => void): SSEClient {
if (!this.eventListeners.has(eventName)) {
this.eventListeners.set(eventName, new Set());
}
this.eventListeners.get(eventName)!.add(listener);
// ...
return this;
}
```
#### 4. Options Pattern
- Uses an options object with defaults for configuration
- Allows for flexible and extensible configuration
```typescript
export interface SSEClientOptions {
headers?: Record<string, string>;
withCredentials?: boolean;
heartbeatTimeout?: number;
// ...
}
const DEFAULT_OPTIONS: Required<Omit<SSEClientOptions, 'headers'>> = {
withCredentials: true,
heartbeatTimeout: 21600000, // 6 hours
// ...
};
```
#### 5. Exponential Backoff Pattern
- Implements exponential backoff for reconnection attempts
- Helps prevent overwhelming the server during outages
```typescript
private async reconnect(): Promise<void> {
this.retryCount++;
// Calculate delay with exponential backoff
const delayMs = Math.min(
this.options.initialRetryDelay * Math.pow(2, this.retryCount - 1),
this.options.maxRetryDelay
);
// ...
}
```
## Critical Implementation Paths
### SSE Client Connection Flow
1. **Initialization**: Create an SSE client with URL and options
2. **Connection**: Call `connect()` to establish the connection
3. **Event Handling**: Register event listeners with `on(eventName, listener)`
4. **Error Handling**: Automatic reconnection with exponential backoff
5. **Cleanup**: Close connection with `close()` or use automatic cleanup
### SSE Client Connection Management
1. **Get/Create Connection**: Use `getSSEConnection(url, id, options)` to get or create a connection
2. **Connection Reuse**: Connections are reused based on the provided ID
3. **Connection Cleanup**: Close specific connections with `closeSSEConnection(id)` or all with `closeAllSSEConnections()`
4. **Automatic Cleanup**: Set up with `setupSSECleanup()` to close connections on page unload
## Component Relationships
- **SSEClient**: Core class that handles the SSE connection and events
- **SSEConnectionManager**: Singleton that manages multiple SSE connections
- **Helper Functions**: Provide simplified access to the connection manager
- `getSSEConnection`: Gets or creates a connection
- `closeSSEConnection`: Closes a specific connection
- `closeAllSSEConnections`: Closes all connections
- `setupSSECleanup`: Sets up automatic cleanup
- **Debug Utilities**: Provide debug logging capabilities
- `debugLog`, `debugInfo`, `debugWarn`, `debugError`