Skip to content

Latest commit

 

History

History
437 lines (330 loc) · 14.4 KB

File metadata and controls

437 lines (330 loc) · 14.4 KB

Serper SDK Architecture Documentation

Overview

The Serper SDK is designed as a highly modular, type-safe Rust SDK for the Serper Google Search API. The architecture follows clean architecture principles with clear separation of concerns, dependency inversion, and a focus on maintainability, testability, and extensibility.

Design Philosophy

Core Principles

  1. Modular Architecture: Independent, focused modules with single responsibilities
  2. Type Safety: Strong typing prevents invalid states and runtime errors
  3. Clean APIs: Well-defined interfaces between modules
  4. Error Transparency: Comprehensive error handling with clear error types
  5. Async First: Native async support throughout with concurrent operations
  6. Zero-Cost Abstractions: Minimal runtime overhead from architectural choices

SOLID Principles Application

  • Single Responsibility: Each module has a single, well-defined purpose
  • Open/Closed: Extensible through builder patterns and configuration
  • Liskov Substitution: Consistent interfaces and behavior
  • Interface Segregation: Focused, minimal interfaces between modules
  • Dependency Inversion: High-level modules don't depend on low-level implementation details

Module Architecture

Layer Overview

┌─────────────────────────────────────────────────────┐
│                   Application Layer                 │
│                   (User Code)                       │
└─────────────────────┬───────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────┐
│                 Search Module                       │
│    ┌─────────────┬──────────────┬─────────────┐    │
│    │   Service   │    Query     │  Response   │    │
│    └─────────────┴──────────────┴─────────────┘    │
└─────────────────────┬───────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────┐
│                  HTTP Module                        │
│    ┌─────────────┬──────────────┬─────────────┐    │
│    │   Client    │  Transport   │   Builder   │    │
│    └─────────────┴──────────────┴─────────────┘    │
└─────────────────────┬───────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────┐
│               Foundation Layer                      │
│  ┌──────────┬──────────┬──────────┬──────────────┐ │
│  │   Core   │  Utils   │  Config  │  (External)  │ │
│  └──────────┴──────────┴──────────┴──────────────┘ │
└─────────────────────────────────────────────────────┘

Module Responsibilities

Foundation Layer

Core Module (core/)

  • Purpose: Foundational types and error handling
  • Responsibilities:
    • Define SerperError enum for all error conditions
    • Provide type-safe wrappers (ApiKey, BaseUrl)
    • Common data structures (Location, Pagination)
    • Result type alias for consistent error handling
  • Key Types: SerperError, Result<T>, ApiKey, BaseUrl, Location, Pagination

Utils Module (utils/)

  • Purpose: Common utilities and helpers
  • Responsibilities:
    • URL validation and manipulation
    • String validation and sanitization
    • Collection utilities (HashMap merging, filtering)
    • Retry logic with exponential backoff
  • Key Functions: Validation helpers, retry logic, collection utilities

Config Module (config/)

  • Purpose: Configuration management and validation
  • Responsibilities:
    • Central configuration structure (SdkConfig)
    • Environment variable integration
    • Configuration validation
    • Builder patterns for flexible configuration
  • Key Types: SdkConfig, SdkConfigBuilder

Transport Layer

HTTP Module (http/)

  • Purpose: HTTP transport abstraction and API client
  • Responsibilities:
    • Low-level HTTP transport (HttpTransport)
    • High-level API client (SerperHttpClient)
    • Request/response handling
    • Authentication and headers
    • Concurrent request management
  • Key Types: HttpTransport, SerperHttpClient, TransportConfig

Application Layer

Search Module (search/)

  • Purpose: High-level search operations and orchestration
  • Responsibilities:
    • Search service orchestration (SearchService)
    • Query construction and validation (SearchQuery)
    • Response parsing and processing (SearchResponse)
    • Business logic for search operations
  • Key Types: SearchService, SearchQuery, SearchResponse, OrganicResult

Data Flow Architecture

Request Flow

Application Code
       │
       ▼
SearchService.search(query)
       │
       ▼ 
SearchQuery validation
       │
       ▼
SerperHttpClient.search(query)
       │
       ▼
HttpTransport.post_json(url, api_key, body)
       │
       ▼
reqwest HTTP request
       │
       ▼
HTTP response
       │
       ▼
JSON parsing → SearchResponse
       │
       ▼
Response validation
       │
       ▼
Return to application

Error Flow

Any Module Error
       │
       ▼
Convert to SerperError
       │
       ▼
Propagate via Result<T>
       │
       ▼
Higher-level error handling
       │
       ▼
Application error handling

API Design Patterns

Builder Pattern

Used extensively for flexible object construction:

// Query building
let query = SearchQueryBuilder::new()
    .query("rust programming")
    .location("San Francisco")
    .country("us") 
    .page(1)
    .build()?;

// Service building  
let service = SearchServiceBuilder::new()
    .api_key("your-key")
    .timeout(Duration::from_secs(60))
    .build()?;

// HTTP client building
let client = SerperHttpClientBuilder::new()
    .api_key(api_key)
    .base_url(custom_url)
    .timeout(Duration::from_secs(90))
    .build()?;

Fluent Interface

Chainable methods for convenient configuration:

let config = SdkConfig::new("api-key".to_string())
    .with_timeout(Duration::from_secs(60))
    .with_max_concurrent(10)
    .with_header("Custom".to_string(), "Value".to_string());

Type-Safe Wrappers

Prevent invalid states at compile time:

// ApiKey prevents empty keys
let api_key = ApiKey::new("".to_string())?; // Compile-time/runtime error

// BaseUrl ensures valid URLs
let url = BaseUrl::new("https://api.example.com".to_string());

Error Handling Architecture

Error Hierarchy

pub enum SerperError {
    // Network/transport errors
    Request(reqwest::Error),
    
    // Parsing/serialization errors  
    Json(serde_json::Error),
    
    // API-specific errors
    Api { message: String },
    
    // Configuration errors
    Config { message: String },
    
    // Input validation errors
    Validation { message: String },
    
    // Authentication errors
    InvalidApiKey,
}

Error Propagation Strategy

  1. Convert at Boundaries: External errors converted to SerperError at module boundaries
  2. Context Preservation: Original error information preserved where possible
  3. Error Classification: Errors classified for appropriate handling strategies
  4. Result Type: Consistent Result<T, SerperError> throughout

Error Recovery

  • Retry Logic: Built-in retry for transient failures
  • Fallback Behavior: Graceful degradation where possible
  • Error Context: Rich error context for debugging

Concurrency Architecture

Async Design

  • Async First: All I/O operations are async
  • Concurrent Requests: Built-in support for concurrent API calls
  • Backpressure: Configurable concurrency limits

Synchronization

// Concurrent searches with semaphore-based limiting
pub async fn search_concurrent(
    &self,
    queries: &[SearchQuery],
    max_concurrent: usize,
) -> Result<Vec<SearchResponse>> {
    let semaphore = Arc::new(Semaphore::new(max_concurrent));
    // ... implementation
}

Thread Safety

  • Send + Sync: All shared types implement Send + Sync
  • Immutable Config: Configuration is immutable after creation
  • Connection Reuse: HTTP client connection pooling

Testing Architecture

Test Organization

tests/
├── unit/               # Module-specific unit tests
│   ├── core/          # Core module tests
│   ├── http/          # HTTP module tests  
│   └── search/        # Search module tests
├── integration/       # Cross-module integration tests
└── e2e/              # End-to-end tests with real API

Test Strategies

  1. Unit Tests: Test individual module functionality in isolation
  2. Integration Tests: Test module interactions with mocks
  3. Property Tests: Test invariants and edge cases
  4. End-to-End Tests: Test complete workflows with real API

Mock Strategy

  • HTTP Mocking: Mock HTTP responses for predictable testing
  • Dependency Injection: Configurable dependencies for testing
  • Test Builders: Convenient test data construction

Performance Architecture

Request Optimization

  • Connection Pooling: Reuse HTTP connections
  • Concurrent Execution: Parallel request processing
  • Request Batching: Efficient handling of multiple queries

Memory Management

  • Zero-Copy: Minimal data copying where possible
  • Streaming: Stream large responses
  • Efficient Serialization: Optimized JSON handling

Caching Strategy

  • Response Caching: Optional response caching (future feature)
  • Connection Caching: HTTP connection reuse
  • Configuration Caching: Immutable configuration objects

Security Architecture

API Key Management

  • Type Safety: ApiKey type prevents accidental exposure
  • Secure Storage: No API key logging or exposure
  • Validation: API key format validation

Transport Security

  • HTTPS Only: Force HTTPS for API communications
  • Certificate Validation: Proper TLS certificate validation
  • Timeout Protection: Request timeouts prevent hanging

Input Validation

  • Sanitization: Input sanitization in utils module
  • Validation: Comprehensive input validation
  • SQL Injection Prevention: Not applicable (API-only)

Extensibility Architecture

Plugin Points

  1. Custom Transport: Pluggable transport implementations
  2. Custom Serialization: Alternative serialization formats
  3. Middleware: Request/response middleware (future)
  4. Custom Retry Logic: Configurable retry strategies

Configuration Extensions

  • Environment Variables: Comprehensive environment variable support
  • Configuration Files: Potential YAML/TOML config support (future)
  • Runtime Configuration: Dynamic configuration updates (future)

API Extensions

  • New Endpoints: Easy addition of new API endpoints
  • Response Types: Extensible response type system
  • Query Parameters: Flexible query parameter system

Deployment Architecture

Packaging

  • Single Crate: All modules in single crate for simplicity
  • Feature Flags: Optional features via Cargo features (future)
  • Minimal Dependencies: Careful dependency selection

Platform Support

  • Cross-Platform: Works on all major platforms
  • Async Runtime: Compatible with Tokio and async-std
  • WASM Support: Potential WebAssembly support (future)

Release Strategy

  • Semantic Versioning: Clear versioning strategy
  • Backwards Compatibility: Stable API guarantees
  • Migration Guides: Clear upgrade paths

Monitoring and Observability

Logging Architecture

  • Structured Logging: JSON-structured logs (future)
  • Log Levels: Appropriate log level usage
  • Request Tracing: Request/response logging (optional)

Metrics

  • Request Metrics: Request count, duration, errors
  • Performance Metrics: Response times, throughput
  • Error Metrics: Error rates by type

Debugging Support

  • Debug Traits: Comprehensive Debug implementations
  • Error Context: Rich error information
  • Request/Response Logging: Optional detailed logging

Future Architecture Evolution

Planned Improvements

  1. Plugin System: More extensible plugin architecture
  2. Caching Layer: Built-in response caching
  3. Metrics Integration: Prometheus/OpenTelemetry integration
  4. Configuration Management: Enhanced configuration options

Scalability Improvements

  1. Connection Pooling: More sophisticated connection management
  2. Load Balancing: Client-side load balancing
  3. Circuit Breaker: Automatic failure handling
  4. Rate Limiting: Built-in rate limiting

API Evolution

  1. GraphQL Support: Potential GraphQL endpoint support
  2. Streaming APIs: Support for streaming responses
  3. Webhooks: Webhook subscription management
  4. Batch Operations: More efficient batch operations

Conclusion

The Serper SDK architecture provides a solid foundation for a maintainable, extensible, and performant API client. The modular design enables independent development and testing of components while maintaining clean interfaces and clear data flow. The architecture is designed to evolve gracefully as requirements change and new features are added.