Files
foxhunt/AGENT_M10_ARCHITECTURE_MATRIX.md
jgrusewski 61801cfd06 feat(deprecation): Complete deprecated code analysis and cleanup preparation
**Wave D Phase 6 - Technical Debt Cleanup (Agent C6)**

## Changes
- Identified deprecated code patterns across codebase
- Analyzed mock repository usage (strategically retained per AGENT_M13)
- Documented deprecation cleanup strategy
- Prepared deprecation removal todos

## Analysis Results
- Mock structs: RETAINED (strategic testing infrastructure)
- Never-read fields: 2 instances in backtesting_service
- Dead code warnings: 35 total across workspace
- databento_old references: None found in active code

## Status
-  Deprecation analysis complete
-  Cleanup execution pending user confirmation
- 📊 Test impact assessment ready

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 00:46:19 +02:00

374 lines
9.7 KiB
Markdown

# Cross-Service Repository Architecture Matrix
## Quick Reference Table
| Feature | Trading Service | Backtesting Service | ML Training Service | API Gateway |
|---------|---|---|---|---|
| **Uses Repositories** | Yes (4) | Yes (3) | Yes (1) | No |
| **Trait Count** | 4 | 4 | 1 | 0 |
| **Mock Pattern** | Inline #[cfg(test)] | Dedicated structs | Stateful RwLock | N/A |
| **Factory Method** | No | Yes | No | N/A |
| **Error Type** | Custom enum | anyhow | anyhow | N/A |
| **Composition** | Direct SQLx | Provider wrapper | Delegation | N/A |
| **DI Pattern** | Constructor | Factory trait | Constructor | N/A |
| **Test Support** | Fair | Excellent | Excellent | N/A |
| **Production Ready** | Yes | Yes | Yes | Yes |
| **Reference Impl** | No | **YES** | No | N/A |
## Code Metrics
| Metric | Trading | Backtesting | ML Training | Total |
|--------|---------|-------------|-------------|-------|
| Trait LOC | 319 | 301 | 63 | 683 |
| Impl LOC | 1,448 | 365 | 230 | 2,043 |
| Mock LOC | Varies | 112 | 102 | 214+ |
| Total LOC | 1,767 | 666+ | 293 | 2,726+ |
| Impl Count | 4 | 4 | 1 | 9 |
| Traits/Impl | 1:1 | 1:1 | 1:1 | 3:9 |
## Decision Tree: Which Service to Model After?
```
Do you need multiple repositories?
├─ Yes → Use Backtesting Service pattern
│ (factory, combined trait, dedicated mocks)
└─ No → Use ML Training Service pattern
(single repo, direct injection, stateful mock)
Do you need environment-based selection?
├─ Yes → Use Backtesting Service pattern with create_repositories()
└─ No → Either pattern is fine
Do you need real data + mocks at runtime?
├─ Yes → Use Backtesting Service pattern
│ (supports both via factory)
└─ No → Either pattern is acceptable
Do you need production-like test behavior?
├─ Yes → Use ML Training pattern
│ (stateful RwLock mocks)
└─ No → Use Backtesting pattern
(simple empty-return mocks)
```
## Repository Trait Checklist
When creating a new repository trait, ensure:
- [ ] `#[async_trait]` macro applied
- [ ] `Send + Sync` bounds included
- [ ] All methods are `async`
- [ ] Return type is `Result<T>` (anyhow or custom)
- [ ] Methods are fine-grained (single responsibility)
- [ ] No database-specific concerns leak into trait
- [ ] Trait is `pub` for use across crates
- [ ] Documented with examples
Example:
```rust
/// Repository trait for domain-specific operations
#[async_trait]
pub trait MyRepository: Send + Sync {
/// Descriptive method documentation
async fn operation(&self, param: Type) -> Result<ReturnType>;
}
```
## Implementation Patterns Comparison
### Pattern A: Direct Database (Trading Service)
```
Business Logic
PostgresTradingRepository (impl TradingRepository trait)
SQLx (direct database queries)
PostgreSQL
```
- Pros: Simple, straightforward
- Cons: Tight coupling to database
- Use: When you control the entire data access layer
### Pattern B: Provider Wrapper (Backtesting Service) ⭐ RECOMMENDED
```
Business Logic
StorageManagerTradingRepository (impl TradingRepository trait)
StorageManager (business logic)
Database/Provider (flexible)
```
- Pros: Loose coupling, flexible, testable, factory-friendly
- Cons: Extra abstraction layer
- Use: New services, multiple data sources, complex logic
### Pattern C: Delegation (ML Training Service)
```
Business Logic
PostgresMlDataRepository (impl MlDataRepository trait)
DatabaseManager (delegates all calls)
SQLx (direct queries via DatabaseManager)
```
- Pros: Simple, but still abstracted
- Cons: Minimal value add over direct access
- Use: When DatabaseManager already exists
## Error Handling Comparison
### Trading Service (Custom)
```rust
pub type TradingServiceResult<T> = Result<T, TradingServiceError>;
pub enum TradingServiceError {
ValidationError { message: String },
TimestampConversion { timestamp: i64 },
NetworkError { details: String },
// ...
}
// Usage
async fn store_order(&self, order: &TradingOrder) -> TradingServiceResult<String> {
Ok(id)
}
```
- Pros: Type-safe, custom error handling
- Cons: Diverges from codebase standard
- Status: **NEEDS MIGRATION** to anyhow
### Backtesting/ML Training (anyhow)
```rust
pub type Result<T> = anyhow::Result<T>;
// Usage
async fn create_training_job(&self, job: &TrainingJobRecord) -> Result<()> {
Ok(())
}
```
- Pros: Standardized, composable, ergonomic
- Cons: Less type-safe error handling
- Status: **STANDARD** for new code
## Dependency Injection Comparison
### Direct Constructor Injection
```rust
pub struct PostgresMlDataRepository {
database: Arc<DatabaseManager>,
}
impl PostgresMlDataRepository {
pub fn new(database: Arc<DatabaseManager>) -> Self {
Self { database }
}
}
// Usage
let repo = Arc::new(PostgresMlDataRepository::new(db_manager));
```
### Factory Function with Environment Selection
```rust
pub async fn create_repositories(storage: Arc<StorageManager>) -> Result<DefaultRepositories> {
let use_dbn = std::env::var("USE_DBN_DATA")
.ok()
.and_then(|v| v.parse::<bool>().ok())
.unwrap_or(false);
let market_data: Box<dyn MarketDataRepository> = if use_dbn {
Box::new(DbnMarketDataRepository::new_with_mappings(mappings).await?)
} else {
Box::new(DataProviderMarketDataRepository::new().await?)
};
Ok(DefaultRepositories { market_data, ... })
}
// Usage
let repos = create_repositories(storage).await?;
```
**Recommendation**: Use factory for services with multiple implementations or env-based selection
## Mock Strategy Comparison
### Approach A: Inline Mocks (Trading)
```rust
#[cfg(test)]
mod tests {
// Mocks defined inline when needed
// Only available during testing
}
```
- Isolation: High
- Reusability: Low
- Boilerplate: Minimal
### Approach B: Dedicated Mock Structs (Backtesting) ⭐ RECOMMENDED
```rust
pub struct MockMarketDataRepository;
#[async_trait]
impl MarketDataRepository for MockMarketDataRepository {
async fn load_historical_data(...) -> Result<Vec<MarketData>> {
Ok(vec![])
}
}
```
- Isolation: Low
- Reusability: High
- Boilerplate: Moderate
- **Advantage**: Can be used in production if needed (feature flag)
### Approach C: Stateful In-Memory (ML Training)
```rust
#[cfg(test)]
pub struct MockMlDataRepository {
jobs: Arc<RwLock<HashMap<Uuid, TrainingJobRecord>>>,
}
#[cfg(test)]
#[async_trait]
impl MlDataRepository for MockMlDataRepository {
async fn create_training_job(&self, job: &TrainingJobRecord) -> Result<()> {
let mut jobs = self.jobs.write().await;
jobs.insert(job.id, job.clone());
Ok(())
}
}
```
- Isolation: Medium
- Reusability: Medium
- Boilerplate: High
- **Advantage**: Realistic test behavior, state verification
**Best Practice**: Use **Approach B** for new services
## Migration Path for Trading Service
To align Trading Service with Backtesting/ML patterns:
### Step 1: Error Type Migration
```rust
// Before
pub type TradingServiceResult<T> = Result<T, TradingServiceError>;
// After
pub type Result<T> = anyhow::Result<T>;
```
### Step 2: Mock Pattern Migration
```rust
// Before: Inline in tests
// After: Dedicated mock structs
pub struct MockTradingRepository;
#[async_trait]
impl TradingRepository for MockTradingRepository { ... }
```
### Step 3: Consider Factory Pattern
```rust
// If multiple repository implementations exist,
// add factory function for DI:
pub async fn create_repositories(pool: PgPool) -> Result<TradingRepositories> {
let trading = Box::new(PostgresTradingRepository::new(pool));
let market_data = Box::new(PostgresMarketDataRepository::new(pool));
// ...
}
```
**Effort**: 2-3 hours, low risk, high value
## Testing Strategy by Service
### Trading Service
```
Unit Tests (per repository impl)
Integration Tests (services + repos)
E2E Tests (full stack)
Mock Support: Inline mocks (added per test)
```
### Backtesting Service (RECOMMENDED APPROACH)
```
Unit Tests (mock repositories with empty returns)
Integration Tests (real repositories + providers)
E2E Tests (end-to-end backtesting runs)
Mock Support: Dedicated mock structs + factory selection
```
### ML Training Service
```
Unit Tests (stateful in-memory mock repositories)
Integration Tests (real postgres repository)
E2E Tests (full training pipeline)
Mock Support: Full in-memory HashMap-backed mock
```
## API Gateway (Why No Repositories)
API Gateway correctly **does NOT use repositories** because:
1. **Network Boundary**: Gateway is the entry point, not data layer
2. **Stateless Design**: Should not hold business state
3. **Proxy Pattern**: Routes to downstream services
4. **Separation of Concerns**:
- ✓ Authentication, authorization
- ✓ Rate limiting, audit logging
- ✓ Request routing, health checks
- ✗ Data persistence, business logic
Example correct structure:
```rust
// API Gateway
pub struct ApiGateway {
trading_client: TradingServiceClient,
backtesting_client: BacktestingServiceClient,
ml_training_client: MlTrainingServiceClient,
auth: AuthLayer,
rate_limiter: RateLimiter,
}
// NO repositories here
// Each downstream service manages its own data
```
---
## Summary: Quick Decision Guide
**For new services:**
1. Choose Backtesting Service as template
2. Use dedicated mock structs
3. Use factory pattern for DI if multiple repos exist
4. Standardize on anyhow::Result<T>
5. Add factory function for environment-based selection
**For existing services:**
1. Keep current implementation (already working)
2. Document patterns in REPOSITORY_PATTERN.md
3. Plan gradual migration to backtesting patterns
4. Start with error type standardization (low risk)
**For API Gateway:**
- Keep as-is (correct by design, no repositories)