Files
GMW/docs/superpowers/specs/2026-07-27-services-refactoring-design.md
T
DeveloperandClaude Opus 4.8 977a6f9653
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 30s
Build & Deploy / build-and-push (backend) (push) Failing after 2m28s
Build & Deploy / build-and-push (proxy) (push) Successful in 2m28s
refactor: rename mascot to chatbot across entire codebase
- Backend: mascot-chat module → chatbot, routes /mascot/chat → /chat
- Shared schema: pgMascotChatMessagesTable → pgChatbotMessagesTable
- Frontend: MascotProvider/useMascot → ChatbotProvider/useChatbot
- Gateway schema: update exports to match shared schema
- Docs: update all .md references (CLAUDE.md, README, specs, plans)
- All API routes, controller names, service classes, types renamed

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 15:00:29 +07:00

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, chatbot-chat.controller.ts use convoluted asyncHandler inside function body (Gaya A)
  • voice.controller.ts, health.controller.ts use clean asyncHandler decorator (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.ts
  • modules/chatbot-chat/chatbot-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 prompts
  • prompts/media-analysis.ts — Image/video analysis prompts
  • prompts/stickers.ts — Sticker analysis prompts
  • prompts/emojis.ts — Custom emoji prompts
  • prompts/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 logic
  • mediaBatchProcessor.ts — All media batching logic
  • simpleFallback.ts — The runSimpleTextFallback function

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 extraction
  • visionAnalyzer.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