- Consolidate all DB schema definitions into packages/shared as single source of truth - Migrate backend from raw SQL to Drizzle ORM across all modules - Extract frontend inline UI into separate component files - Refactor discord-gateway circuitBreaker into conversationState + moderationState - Convert messageStore to Proxy singleton pattern - Add validateBody/validateQuery middleware + Zod schemas for API endpoints - Modernize Docker builds with multi-stage + pnpm deploy - Migrate CI/CD from deployment to image-based pipeline - Remove 60+ unused/dead files (~15K lines) - Update color scheme from sky-blue to teal-cyan - Move DB connection management to @bete/shared/database Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.5 KiB
Refactoring Backend & Discord-Gateway Services
Date: 2026-07-27 Status: Draft
Overview
Comprehensive refactoring of services/backend (4.2k lines) and services/discord-gateway (17.9k lines) targeting code consistency, file-size reduction, deduplication, and pattern uniformity.
Scope
Phase 1 — Backend Controller Consistency
Problem: Two competing controller patterns.
messages.controller.ts,mascot-chat.controller.tsuse convolutedasyncHandlerinside function body (Gaya A)voice.controller.ts,health.controller.tsuse cleanasyncHandlerdecorator (Gaya B)
Fix: Convert all controllers to Gaya B (decorator pattern).
Before (Gaya A):
export function handleListMessages(req, res, next) {
return asyncHandler(async (req, res) => {
// ...
})(req, res, next);
}
After (Gaya B):
export const handleListMessages = asyncHandler(async (req, res) => {
// ...
});
Files affected:
modules/messages/messages.controller.tsmodules/mascot-chat/mascot-chat.controller.ts
Phase 2 — Backend response.ts Cleanup
Problem: success()/error() helpers exist but are unused (except health controller).
Fix: Apply success() consistently to all API responses that are successful data returns. Remove error() if unused after audit.
Files affected: All route/service files that res.json() data.
Phase 3 — Backend ws/ Barrel
Problem: ws/broadcast.ts, ws/redis-bridge.ts, ws/server.ts — no barrel.
Fix: Add ws/index.ts barrel.
Phase 4 — Gateway: Split moderationPrompt.ts (1015 lines)
Problem: Monolithic prompt file mixing all prompt types.
Fix: Split into:
prompts/text-analysis.ts— Text moderation promptsprompts/media-analysis.ts— Image/video analysis promptsprompts/stickers.ts— Sticker analysis promptsprompts/emojis.ts— Custom emoji promptsprompts/system.ts— System prompt builder and shared helpers
Phase 5 — Gateway: Split moderationOrchestrator.ts (955 lines)
Problem: Entry point that also contains inline text-only batch, media batch, and simple fallback.
Fix: Extract into:
textBatchProcessor.ts— All text-only batching logicmediaBatchProcessor.ts— All media batching logicsimpleFallback.ts— TherunSimpleTextFallbackfunction
Phase 6 — Gateway: Split mediaAnalysisClient.ts (826 lines)
Problem: Cache logic (LRU + phash + DB), download logic (image/video + ffmpeg), and vision LLM in one file.
Fix: Extract into:
mediaCache.ts— All caching layers (LRU, phash dedup, DB)mediaDownloader.ts— Image/video download, ffmpeg frame extractionvisionAnalyzer.ts— Vision LLM orchestration
Phase 7 — Gateway: Consolidate bootstrap.ts
Problem: 304-line bootstrap that embeds retention cleanup inline.
Fix: Extract startRetentionCleanup into app/retention.ts. Leave event registrations in bootstrap as they're inherently app-wide wiring.
Phase 8 — Gateway: Simplify EventBroadcaster
Problem: RedisEventPublisher wrapping is thin — only adds a publish wrapper.
Fix: Merge RedisEventPublisher into EventBroadcaster as a private inner detail.
Phase 9 — Cross-cutting: Database initialization dedup
Problem: Backend (shared/database/index.ts) and gateway (shared/database/drizzle.ts) have near-identical pool creation and lifecycle code.
Fix: Extract common pool/drizzle lifecycle into @bete/shared:
// packages/shared/src/database/index.ts
export function createDatabasePool(url: string, opts?: PoolOpts): Pool
export function createDrizzleClient(pool: Pool): DrizzleClient
export function closePool(pool: Pool): Promise<void>
Both services keep their own getDatabase/close wrappers but delegate pool creation to shared.
Phase 10 — Gateway: Consolidate moderationState.ts / conversationState.ts
Problem: Two state files with overlapping concerns.
Fix: Audit both for overlap, merge if significant duplication found.
Phase 11 — Gateway: Redis connection usage audit
Problem: Multiple independent Redis connections for EventBroadcaster and CommandHandler.
Fix: Both already need separate connections (Redis pub/sub limits). Document the pattern. No structural change.
Files Changed
| Phase | Files | Type |
|---|---|---|
| 1 | 3 | edit |
| 2 | ~15 | edit |
| 3 | 1 | create |
| 4 | ~6 | split |
| 5 | ~4 | split |
| 6 | ~4 | split |
| 7 | 2 | split |
| 8 | 2 | refactor |
| 9 | 2 | refactor |
| 10 | 1-2 | audit+merge |