Skip to content

docs: Document adapter pattern best practices for dependency management #279

Description

@kcenon

Summary

Document database_system's adapter pattern as a best practice reference for managing optional dependencies across the ecosystem.

5W1H Specification

  • Who: database_system maintainers, ecosystem documentation
  • What: Document the runtime backend selection pattern used by database_system
  • Where: docs/ADAPTER_PATTERNS.md, README.md
  • When: v0.4.0.0 release cycle
  • Why:
    • database_system has the most sophisticated optional dependency handling
    • Pattern achieves ZERO circular dependency risk despite many integrations
    • Other modules can adopt same pattern
  • How: Document patterns with code examples

Priority

LOW - Documentation task, no code changes required

Current State Analysis

database_system demonstrates excellent optional dependency management:

database_system (Tier 3)
       │
       ├── common_system (REQUIRED)
       ├── thread_system (OPTIONAL - adapter pattern)
       ├── container_system (OPTIONAL - protocol container)
       ├── monitoring_system (OPTIONAL - metrics adapter)
       └── External: PostgreSQL, MySQL, SQLite, MongoDB, Redis

Risk Assessment: NONE

  • All optional dependencies use runtime backend selection
  • PIMPL idiom hides implementation dependencies
  • Factory patterns allow late binding

Adapter Pattern Examples

1. Monitoring Adapter Pattern

// database_system/integrated/adapters/monitoring_adapter.h

class monitoring_backend {
public:
    virtual ~monitoring_backend() = default;
    virtual void record_query_time(std::chrono::nanoseconds) = 0;
    virtual void record_connection_acquired() = 0;
    virtual void record_error(std::string_view) = 0;
};

// Fallback when monitoring_system unavailable
class fallback_monitoring_backend : public monitoring_backend {
public:
    void record_query_time(std::chrono::nanoseconds ns) override {
        total_query_time_ += ns;
        query_count_++;
    }
    // Internal storage, no external dependency
private:
    std::atomic<std::size_t> query_count_{0};
    std::atomic<std::chrono::nanoseconds::rep> total_query_time_{0};
};

#if USE_MONITORING_SYSTEM
class system_monitoring_backend : public monitoring_backend {
    // Uses monitoring_system for real metrics
};
#endif

// Factory selects at runtime
auto create_monitoring_backend() -> std::unique_ptr<monitoring_backend> {
#if USE_MONITORING_SYSTEM
    if (monitoring_available()) {
        return std::make_unique<system_monitoring_backend>();
    }
#endif
    return std::make_unique<fallback_monitoring_backend>();
}

2. Thread Pool Adapter Pattern

// database_system/adapters/thread_pool_adapter.h

class thread_backend {
public:
    virtual ~thread_backend() = default;
    virtual void submit(std::function<void()> task) = 0;
};

// Fallback using std::thread
class std_thread_backend : public thread_backend {
    void submit(std::function<void()> task) override {
        std::thread(std::move(task)).detach();
    }
};

#if USE_THREAD_SYSTEM
class thread_system_backend : public thread_backend {
    void submit(std::function<void()> task) override {
        pool_->submit(std::move(task));
    }
};
#endif

Documentation Structure

Proposed docs/ADAPTER_PATTERNS.md

  1. Pattern Overview

    • When to use adapter pattern vs direct dependency
    • Benefits: testability, isolation, flexibility
  2. Implementation Checklist

    - [ ] Define abstract backend interface
    - [ ] Implement fallback backend (no external deps)
    - [ ] Implement real backend with #if guard
    - [ ] Create factory function for runtime selection
    - [ ] Use PIMPL to hide implementation details
  3. Anti-Patterns to Avoid

    • Direct #include without interface abstraction
    • Compile-time only switching (no runtime fallback)
    • Fallback that silently drops functionality
  4. Code Examples

    • Monitoring adapter
    • Thread pool adapter
    • Logger adapter

Tasks

  • Create docs/ADAPTER_PATTERNS.md
  • Document monitoring adapter pattern with code
  • Document thread pool adapter pattern
  • Add "when to use" decision tree
  • Create anti-patterns section
  • Link from ecosystem documentation
  • Add adapter pattern checklist

Best Practices Checklist

## Adapter Pattern Checklist

### Interface Design
- [ ] Abstract base class with virtual destructor
- [ ] Pure virtual methods for all operations
- [ ] No dependency on external types in interface

### Fallback Implementation
- [ ] Works without any optional dependencies
- [ ] Provides meaningful (not stub) functionality
- [ ] Can be used for testing

### Real Implementation
- [ ] Guarded with `#if USE_*` preprocessor
- [ ] Uses external dependency internally only
- [ ] Same interface as fallback

### Factory
- [ ] Runtime detection of dependency availability
- [ ] Returns fallback when unavailable
- [ ] Single point of backend creation

Acceptance Criteria

  • docs/ADAPTER_PATTERNS.md exists with examples
  • Checklist can be used by other modules
  • Anti-patterns documented
  • Linked from ecosystem documentation

Dependencies

  • None - Documentation only

Parent Epic

Related Issues

  • Documents patterns used in monitoring_adapter, thread_pool_adapter, logger_adapter

Metadata

Metadata

Assignees

Labels

architectureArchitectural changes and designdocumentationImprovements or additions to documentation

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions