Refactor Codebase Based on Best Practices and Clean/Hexagonal Architecture
Problem Statement
The current codebase has architectural issues that make it difficult to maintain and test:
- Fat components - Business logic mixed with UI components (see
components/RandomAd.vue)
- Tight coupling - API calls, data parsing, and validation in UI layer
- Hard to test - Business logic requires mounting Vue components
- Configuration leaks - Runtime config accessed directly in components
- Security concerns -
postMessage to wildcard origin (*)
These issues are documented in project-audit.md.
Goal
Refactor the codebase to follow Clean Architecture (Hexagonal Architecture) principles:
- Separation of Concerns - Clear boundaries between layers
- Testability - Business logic testable without UI
- Maintainability - Easier to understand and modify
- Flexibility - Easy to swap implementations (API, storage, etc.)
Proposed Architecture
Four Layers:
┌─────────────────────────────────────┐
│ Presentation (Vue components) │ ← Rendering only
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Application (Use cases) │ ← Business workflows
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Infrastructure (API, config) │ ← External services
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Domain (Business logic) │ ← Pure logic, no framework
└─────────────────────────────────────┘
Dependencies flow inward - outer layers depend on inner layers, never reverse.
Implementation Plan
Phase 1: Domain Layer (Low Risk - 8-10 hours)
Phase 2: Application Layer (Medium Risk - 10-12 hours)
Phase 3: Infrastructure Layer (Low Risk - 8-10 hours)
Phase 4: Presentation Layer (High Risk - 12-15 hours)
Phase 5: Security & Best Practices (Medium Risk - 6-8 hours)
Phase 6: Documentation (Low Risk - 6-8 hours)
Success Criteria
Code Quality:
Functional:
Developer Experience:
Example: Before & After
Before (Current):
// components/RandomAd.vue - 300+ lines
const pickRandomAd = async () => {
state.isLoading = true
const url = `${runtimeConfig.public.adsServer}/api/ads?${params}`
const apiResponse = await $fetch<IResponseFetchAd>(url)
if (apiResponse && isAd(apiResponse)) {
state.whichAdToShow = {
adType: apiResponse.ad_type,
height: parseInt(apiResponse.height),
// ... more parsing
}
}
state.isLoading = false
}
After (Proposed):
// components/RandomAd.vue - ~50 lines
<script setup>
const { ad, loading, error, fetchAd } = useAdService()
onMounted(() => fetchAd(query))
</script>
<template>
<AdLoader v-if="loading" />
<AdError v-else-if="error" :error="error" />
<AmazonBanner v-else-if="ad?.type === 'AmazonBanner'" :ad="ad" />
</template>
Migration Strategy
- Incremental - Implement one phase at a time
- Backward compatible - Old and new code coexist during migration
- Testable - Each phase independently testable
- Can pause - Stop after any phase if needed
Estimated Effort
Total: 40-60 hours (8-12 days part-time)
Can be broken into smaller PRs for each phase.
References
Discussion
Questions to consider:
- Is this appropriate for a relatively small codebase?
- What's the priority vs other features?
- Should we implement all phases or just 1-3?
- Impact on bundle size?
Priority: Medium (Technical Debt)
Type: Enhancement, Architecture, Refactoring
Refactor Codebase Based on Best Practices and Clean/Hexagonal Architecture
Problem Statement
The current codebase has architectural issues that make it difficult to maintain and test:
components/RandomAd.vue)postMessageto wildcard origin (*)These issues are documented in
project-audit.md.Goal
Refactor the codebase to follow Clean Architecture (Hexagonal Architecture) principles:
Proposed Architecture
Four Layers:
Dependencies flow inward - outer layers depend on inner layers, never reverse.
Implementation Plan
Phase 1: Domain Layer (Low Risk - 8-10 hours)
domain/ads/types.ts- Ad entities with discriminated unionsdomain/ads/validators.ts- Validation logicdomain/shared/result.ts- Result<T, E> type for error handlingdomain/shared/errors.ts- Domain-specific errorsPhase 2: Application Layer (Medium Risk - 10-12 hours)
application/ports/IAdRepository.ts- Interface for data accessapplication/ports/IConfigProvider.ts- Interface for configurationapplication/use-cases/FetchRandomAd.ts- Fetch and validate ad use casePhase 3: Infrastructure Layer (Low Risk - 8-10 hours)
infrastructure/api/LaravelAdRepository.ts- Laravel API implementationinfrastructure/api/mappers.ts- Map API responses to domain modelsinfrastructure/config/RuntimeConfigProvider.ts- Nuxt config wrapperPhase 4: Presentation Layer (High Risk - 12-15 hours)
composables/useAdService.ts- Wraps use cases for Vuecomponents/RandomAd.vueto use composablePhase 5: Security & Best Practices (Medium Risk - 6-8 hours)
*)Phase 6: Documentation (Low Risk - 6-8 hours)
Success Criteria
Code Quality:
domain/andapplication/)anytypes in production codeFunctional:
Developer Experience:
Example: Before & After
Before (Current):
After (Proposed):
Migration Strategy
Estimated Effort
Total: 40-60 hours (8-12 days part-time)
Can be broken into smaller PRs for each phase.
References
project-audit.mdDiscussion
Questions to consider:
Priority: Medium (Technical Debt)
Type: Enhancement, Architecture, Refactoring