# Migration Plan: Hexagonal Architecture for TTS-Groq
## 1. Overview
This document outlines a step-by-step plan to migrate TTS-Groq to a robust hexagonal (ports & adapters) architecture, leveraging Rust's trait system for maximum modularity, testability, and extensibility.
---
## 2. Hexagonal Architecture Principles (Rust Context)
- **Domain/Core:** Pure business logic, no dependencies on external systems. All dependencies are expressed as traits (ports).
- **Ports:** Traits defining interfaces for inbound (driven by) and outbound (driving) adapters.
- **Adapters:** Implementations of ports (e.g., for providers, audio devices, platform insertion, hotkey handling).
- **Dependency Direction:** Domain depends only on traits/ports; adapters depend on domain traits, not vice versa.
- **Testability:** Core logic can be tested with mock adapters.
---
## 3. Mapping TTS-Groq to Hexagonal Layers
- **Domain Layer:** Core transcription logic, state, and use cases (traits and structs).
- **Application Layer:** Orchestrates domain logic, manages tasks, coordinates adapters.
- **Ports:**
- Inbound: Hotkey events, CLI/config, user commands.
- Outbound: Audio capture, transcription provider, text insertion, logging, shutdown.
- **Adapters:**
- Inbound: Hotkey service, CLI parser.
- Outbound: Audio device manager, provider implementations, platform text inserter, logging, shutdown handler.
---
## 4. Step-by-Step Migration Plan
### A. Inventory and Refine Traits (Ports)
- List all traits: `TranscriptionProvider`, `TextInserter`, `TranscriptionResultHandler`, etc.
- Add/Refine traits for:
- Audio capture
- Hotkey event source
- Shutdown signaling
### B. Create `domain/` Module
- Move all core business logic and trait definitions here.
- Ensure domain logic depends only on traits, not concrete implementations.
### C. Create `adapters/` Module
- Move all platform, provider, audio, hotkey, and shutdown implementations here, grouped by concern.
- Each adapter implements the corresponding port (trait).
### D. Refactor Application Layer
- `app.rs` becomes the application service, orchestrating domain and adapters.
- Application layer wires together domain logic and adapters via trait objects.
### E. Implement Dependency Injection
- Use constructor injection for adapters (pass trait objects to domain/application structs).
- Use `Arc<dyn Trait + Send + Sync>` for async/multithreaded contexts as needed.
### F. Testing
- Provide mock implementations for all ports for unit/integration testing.
- Ensure all domain logic is testable without real adapters.
### G. Proposed Directory Structure
```
src/
domain/
mod.rs
transcription.rs
audio.rs
hotkey.rs
provider.rs
platform.rs
shutdown.rs
application/
app.rs
orchestrator.rs
adapters/
audio/
cpal_audio.rs
provider/
groq.rs
openai.rs
mock.rs
platform/
windows.rs
linux.rs
macos.rs
stub.rs
hotkey/
hotkey_service.rs
shutdown/
shutdown_handler.rs
main.rs
```
### H. Example Trait (Port) Definition
```rust
// domain/provider.rs
pub trait TranscriptionProvider: Send + Sync {
fn transcribe(&self, audio_chunk: AudioChunk) -> Result<TranscriptionResult, ProviderError>;
}
```
### I. Example Adapter Implementation
```rust
// adapters/provider/groq.rs
use crate::domain::provider::{TranscriptionProvider, AudioChunk, TranscriptionResult, ProviderError};
pub struct GroqProvider { /* fields */ }
impl TranscriptionProvider for GroqProvider {
fn transcribe(&self, audio_chunk: AudioChunk) -> Result<TranscriptionResult, ProviderError> {
// Implementation
}
}
```
### J. Application Layer Wiring
```rust
// application/app.rs
use crate::domain::provider::TranscriptionProvider;
pub struct App {
provider: Box<dyn TranscriptionProvider>,
// other fields
}
impl App {
pub fn new(provider: Box<dyn TranscriptionProvider>, /* ... */) -> Self {
// ...
}
}
```
---
## 5. Migration Steps
1. **Inventory Existing Traits:** List all traits and identify missing ports.
2. **Create `domain` Module:** Move/refactor all domain logic and trait definitions here.
3. **Create `adapters` Module:** Move all concrete implementations here, grouped by concern.
4. **Refactor Application Layer:** Ensure orchestration is via trait objects only.
5. **Update Dependency Injection:** Refactor constructors to accept trait objects.
6. **Write/Update Tests:** Use mocks for all ports; ensure coverage of edge cases.
7. **Iterate and Validate:** Refactor incrementally, running tests after each step.
8. **Document Migration:** Update documentation to reflect new architecture.
---
## 6. Boundary Cases and Robustness
- Ensure all async boundaries are handled with trait bounds (`Send + Sync`).
- Graceful shutdown: propagate cancellation through trait-based signals.
- Hotkey and audio device errors: handle and propagate via Result types.
- Platform-specific adapters: ensure fallback/stub implementations for unsupported platforms.
- Test with multiple providers and platforms, including failure scenarios.
---
## 7. Testing and Validation
- Write integration tests that use mock adapters to simulate all edge cases.
- Test shutdown and error propagation paths thoroughly.
- Validate that new providers and platforms can be added with minimal changes.
---
## 8. References
- See `docs/README.md`, `docs/modules.md`, and `docs/architecture.md` for current design and module details.
---
**Follow this plan incrementally and rigorously test after each step to ensure a robust migration to hexagonal architecture.**