Skip to content

Layer Boundaries

This document defines the clear boundaries between the architectural layers in the ReX system, establishing responsibilities, interfaces, and interaction patterns for each layer.

System Architecture Overview

The ReX system is organized into the following layers, from highest to lowest level:

Each layer has distinct responsibilities and communicates with adjacent layers through well-defined interfaces. This separation ensures:

  1. Maintainability: Changes in one layer don’t ripple through the entire system
  2. Testability: Each layer can be tested in isolation with appropriate mocks
  3. Extensibility: New implementations can be added at each layer without affecting others
  4. Adaptability: The system can adapt to different environments by swapping layer implementations

Application Layer

The Application Layer provides user-facing abstractions and integration with UI frameworks.

Responsibilities

  • Integrate with React through hooks and components
  • Provide declarative access to content operations
  • Handle UI-specific concerns like loading states and error presentation
  • Manage component lifecycle in relation to content subscriptions
  • Present content in appropriate formats for display

Key Components

React Hooks

typescript
function useContent(uri: string): {
  content: Content | null
  loading: boolean
  error: Error | null
  refresh: () => Promise<void>
}

function useContentWrite(): {
  write: (uri: string, content: Content) => Promise<void>
  writing: boolean
  error: Error | null
}

function useContentList(query?: ContentQuery): {
  uris: string[]
  loading: boolean
  error: Error | null
}

React Components

typescript
interface ContentDisplayProps {
  uri: string
  fallback?: React.ReactNode
  errorComponent?: React.ComponentType<{ error: Error }>
  transformContent?: (content: Content) => Content
}

interface ContentEditorProps {
  uri: string
  initialContent?: Content
  onSave?: (content: Content) => void
  autoSave?: boolean
  saveInterval?: number
}

Interface With Store Layer

  • Consumes: ContentStore instance and operations
  • Provides: React integration for content access and manipulation
  • Pattern: Hooks wrap store operations with React lifecycle management

Implementation Patterns

  • Subscription Management: Hooks subscribe to content changes and clean up on unmount
  • Error Boundaries: Components use React error boundaries for graceful failure
  • Progressive Enhancement: Components adapt to available capabilities
  • Separation of Concerns: Data access through hooks, presentation through components

Store Layer

The Store Layer provides high-level content operations and serves as the primary API for application code.

Responsibilities

  • Expose unified content operations (read, write, delete, list)
  • Coordinate middleware pipeline for operations
  • Provide content change notification system
  • Support content Uri resolution and normalization
  • Apply content transformations as needed

Key Components

Content Store

typescript
interface ContentStore {
  // Core operations
  read<T = string>(uri: string): Promise<Content<T>>
  write<T = string>(uri: string, content: Content<T>): Promise<void>
  delete(uri: string): Promise<void>
  list(query?: ContentQuery): Promise<string[]>

  // Change notification
  onChange(listener: ContentChangeListener): UnsubscribeFunction

  // Resource management
  dispose(): Promise<void>
}

type ContentChangeListener = (
  uri: string,
  content: Content | null,
  changeType: ChangeType
) => void

type ChangeType = 'created' | 'updated' | 'deleted' | 'moved'

Content Registry

typescript
interface ContentRegistry {
  // Store management
  getStore(id?: string): ContentStore
  createStore(options?: StoreOptions): ContentStore
  registerStore(id: string, store: ContentStore): void

  // Global operations
  getContent<T = string>(uri: string): Promise<Content<T>>
  listContent(query?: ContentQuery): Promise<string[]>

  // Resource management
  dispose(): Promise<void>
}

Interface With Adjacent Layers

  • Consumes: Middleware-enhanced adapter operations
  • Provides: High-level content operations to applications
  • Pattern: Factory functions create stores with appropriate middleware

Implementation Patterns

  • Factory Functions: createContentStore() configures store with appropriate adapters and middleware
  • Registry Pattern: ContentRegistry manages multiple stores for complex applications
  • Observer Pattern: Change notification system allows reactive updates
  • Configuration Objects: Options pattern for flexible store creation

Middleware Layer

The Middleware Layer provides cross-cutting concerns and function enhancement.

Responsibilities

  • Implement cross-cutting concerns like caching, validation, logging
  • Transform content between operations
  • Enhance adapter behavior without modifying adapters
  • Provide composition utilities for building middleware chains
  • Implement operation hooks for before/after processing

Key Components

Middleware Definition

typescript
type Middleware<T = any> = (
  next: (uri: string, options?: OperationOptions) => Promise<T>
) => (uri: string, options?: OperationOptions) => Promise<T>

interface OperationOptions {
  signal?: AbortSignal
  [key: string]: unknown
}

Middleware Factories

typescript
function withCaching(options?: CacheOptions): Middleware
function withValidation(schema: ValidationSchema): Middleware
function withLogging(options?: LogOptions): Middleware
function withErrorHandling(handlers: ErrorHandlers): Middleware

Composition Utilities

typescript
function pipe<T>(...fns: Array<(arg: T) => T>): (arg: T) => T
function compose<T>(...fns: Array<(arg: T) => T>): (arg: T) => T

Interface With Adjacent Layers

  • Consumes: Adapter operations
  • Provides: Enhanced operations to the Store Layer
  • Pattern: Function composition using higher-order functions

Implementation Patterns

  • Higher-Order Functions: Middleware are functions that return functions
  • Function Composition: pipe() and compose() utilities for combining middleware
  • Options Objects: Configuration through factory function parameters
  • Context Passing: OperationOptions for sharing context between middleware

Adapter Layer

The Adapter Layer provides an abstraction over storage mechanisms with a consistent interface.

Responsibilities

  • Abstract storage details behind a consistent interface
  • Handle environment-specific storage implementations
  • Provide capability-based feature detection
  • Implement serialization for storage-specific formats
  • Handle storage-specific error mapping

Key Components

Adapter Interface

typescript
interface ContentAdapter<T = string> {
  // Core operations
  read(uri: string, options?: OperationOptions): Promise<Content<T>>
  write(
    uri: string,
    content: Content<T>,
    options?: OperationOptions
  ): Promise<void>
  delete(uri: string, options?: OperationOptions): Promise<void>
  list(query?: ContentQuery, options?: OperationOptions): Promise<string[]>

  // Capability information
  capabilities: AdapterCapability[]

  // Optional advanced operations
  move?(
    uri: string,
    targetUri: string,
    options?: OperationOptions
  ): Promise<void>
  copy?(
    uri: string,
    targetUri: string,
    options?: OperationOptions
  ): Promise<void>
  watch?(uri: string, callback: WatchCallback): UnsubscribeFunction

  // Events (optional)
  events?: EventEmitter

  // Resource management
  dispose(): Promise<void>
}

type AdapterCapability =
  | 'read'
  | 'write'
  | 'delete'
  | 'list'
  | 'move'
  | 'copy'
  | 'watch'

Adapter Factories

typescript
function createMemoryAdapter(options?: MemoryAdapterOptions): ContentAdapter
function createFileSystemAdapter(
  options?: FileSystemAdapterOptions
): ContentAdapter
function createIndexedDBAdapter(
  options?: IndexedDBAdapterOptions
): ContentAdapter
function createHttpAdapter(options?: HttpAdapterOptions): ContentAdapter

Interface With Adjacent Layers

  • Consumes: Storage Layer operations
  • Provides: Storage abstraction to Middleware Layer
  • Pattern: Factory functions create adapters with appropriate options

Implementation Patterns

  • Factory Functions: Adapter creation with consistent patterns
  • Capability Declaration: Explicit capability listing for feature detection
  • Serialization Handling: Standardized approach to content serialization
  • Error Mapping: Storage-specific errors mapped to standard ContentError types

Storage Layer

The Storage Layer handles direct interaction with physical storage mechanisms.

Responsibilities

  • Provide direct access to storage mechanisms (filesystem, IndexedDB, etc.)
  • Handle environment-specific storage operations
  • Implement low-level serialization and deserialization
  • Manage storage-specific resource lifecycle
  • Handle storage-specific error handling

Key Components

Environment-Specific Storage

  • Node.js: File system operations, fs/promises API
  • Browser: IndexedDB, localStorage, sessionStorage
  • Service Worker: Cache API, IndexedDB
  • Remote: Fetch API, XMLHttpRequest

Serialization Utilities

typescript
function serializeContent<T>(content: Content<T>): string | Uint8Array
function deserializeContent<T>(
  data: string | Uint8Array,
  contentType: string
): Content<T>

Error Handling

typescript
function mapStorageError(error: unknown, uri: string): ContentError

Interface With Adapter Layer

  • Consumes: N/A (lowest layer)
  • Provides: Storage operations to Adapter Layer
  • Pattern: Environment detection for appropriate implementation selection

Implementation Patterns

  • Environment Detection: Utilities like isNode(), isBrowser() for implementation selection
  • Capability Testing: Feature detection for available APIs
  • Error Mapping: Storage-specific errors mapped to standard error types
  • Resource Cleanup: Proper handling of connection closing, file descriptors, etc.

Cross-Layer Considerations

Error Handling

Error handling follows a consistent pattern across layers:

  1. Storage Layer: Creates native storage errors
  2. Adapter Layer: Maps storage errors to ContentError types
  3. Middleware Layer: May handle, transform, or enhance errors
  4. Store Layer: Provides consistent error handling for content operations
  5. Application Layer: Presents errors in UI-appropriate formats
typescript
class ContentError extends Error {
  uri: string
  source: string
  recoverable: boolean
  cause?: unknown
}

class ContentNotFoundError extends ContentError {}
class ContentAccessError extends ContentError {}
class ContentValidationError extends ContentError {}
class ContentFormatError extends ContentError {}

Environment Adaptability

The system adapts to different environments through:

  1. Environment Detection: Utilities detect environment capabilities
  2. Conditional Imports: Import environment-specific modules as needed
  3. Capability Detection: Runtime testing for available features
  4. Progressive Enhancement: Core functionality with optional enhancements
  5. Fallback Mechanisms: Graceful degradation when features unavailable

Type Safety

Type safety is maintained through:

  1. Generic Types: Content<T> for different content data types
  2. Type Transformations: Explicit transformation functions between types
  3. Type Guards: Runtime type checking where needed
  4. Interface Consistency: Consistent interfaces across layers
  5. Typescript Strictness: Strict mode and thorough type checking

Implementation Strategy

When implementing or modifying components, follow these principles:

  1. Respect Layer Boundaries: Components should only depend on adjacent layers
  2. Interface Stability: Public interfaces should be stable and backward compatible
  3. Implementation Flexibility: Implementations can change as long as interfaces remain stable
  4. Testing Isolation: Tests should mock adjacent layer dependencies
  5. Documentation Clarity: Document which layer a component belongs to

Released under the MIT License.