- Extract services into services/{frontend,backend,discord-gateway}
- Create packages/shared/ for shared logger, errors, utils, types
- Setup Modular MVC pattern in backend (controller→service→repository)
- Setup event-driven architecture in discord-gateway with Redis pub/sub
- Move Docker files to infra/docker/ with per-service Dockerfiles
- Update docker-compose.yml to use Traefik-only routing (no port exposes)
- Update GitHub Actions deploy workflow for multi-service matrix build
- Fix all import paths and resolve type errors across all services
- All 3 services pass tsc --noEmit clean
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.4 KiB
services/discord-gateway/ ├── src/ │ ├── app/ │ │ ├── bootstrap.ts # Discord Gateway initialization (no HTTP server) │ │ └── shutdown.ts # Graceful shutdown handler │ ├── shared/ │ │ ├── config/ │ │ │ └── config.ts # Environment configuration (Zod validated) │ │ ├── database/ │ │ │ ├── schema.ts # Drizzle ORM schema │ │ │ ├── drizzle.ts # Database connection │ │ │ ├── migrate.ts # Migration runner │ │ │ └── voiceRecordingRepo.ts │ │ ├── errors/ │ │ │ └── errors.ts # Custom error classes │ │ ├── logger/ │ │ │ ├── logger.ts # Winston logger wrapper │ │ │ └── serialization.ts # Log value serialization │ │ ├── utils/ │ │ │ └── retry.ts # Retry with backoff utility │ │ └── discord/ │ │ └── clientOptions.ts # Discord.js client configuration │ ├── modules/ │ │ ├── message-capture/ # Modular MVC: Message capture & storage │ │ │ ├── messageCapture.ts # Controller: Discord event listeners │ │ │ ├── messageStore.ts # Repository: Database operations │ │ │ ├── messageMetadata.ts # Service: Message metadata extraction │ │ │ ├── types.ts # Domain types │ │ │ └── index.ts # Module exports │ │ ├── ai-moderation/ # Modular MVC: AI analysis & moderation │ │ │ ├── aiAnalyzer.ts # Controller: Analysis orchestration │ │ │ ├── llmModerationClient.ts # Service: LLM API client │ │ │ ├── aiAnalysisWorker.ts # Service: Worker pool management │ │ │ ├── indonesianTextNormalizer.ts # Service: Text normalization │ │ │ ├── moderationPrompt.ts # Service: Prompt generation │ │ │ └── index.ts # Module exports │ │ ├── voice-recording/ # Modular MVC: Voice recording & streaming │ │ │ ├── voiceController.ts # Controller: Voice connection management │ │ │ ├── recorder.ts # Service: Recording orchestration │ │ │ ├── recorder/ │ │ │ │ ├── audioStream.ts # Service: Audio stream subscription │ │ │ │ ├── decoder.ts # Service: Opus decoding │ │ │ │ ├── segment.ts # Service: OGG segment rotation │ │ │ │ ├── metadata.ts # Service: Segment metadata │ │ │ │ ├── sessionRecording.ts # Service: Session management │ │ │ │ └── uploader.ts # Service: Segment upload │ │ │ └── index.ts # Module exports │ │ ├── attachment-upload/ # Modular MVC: Attachment handling │ │ │ ├── attachmentUploader.ts # Service: Upload orchestration │ │ │ ├── imageResizer.ts # Service: Image resizing │ │ │ └── index.ts # Module exports │ │ └── event-broadcaster/ # Event-driven: Redis pub/sub │ │ ├── eventBroadcaster.ts # Service: Event publishing │ │ ├── eventTypes.ts # Domain: Event type definitions │ │ └── index.ts # Module exports │ ├── mock-crc.ts # CRC polyfill for discord.js │ └── index.ts # Service entry point ├── package.json # Service dependencies └── tsconfig.json # TypeScript configuration
Architecture Patterns
Modular MVC Structure
Each module follows Controller-Service-Repository pattern:
- Controller: Discord event listeners (messageCapture, aiAnalyzer, voiceController)
- Service: Business logic (messageStore, llmModerationClient, recorder)
- Repository: Data access (messageStore, voiceRecordingRepo)
Event-Driven Design
- Redis Pub/Sub: All events published to Redis channels
- Event Channels:
discord:message:created— New message captureddiscord:message:updated— Message editeddiscord:message:deleted— Message deleteddiscord:message:analyzed— AI analysis completediscord:attachment:created— Attachment detecteddiscord:attachment:uploaded— Attachment uploaded to storagediscord:voice:started— Voice recording starteddiscord:voice:stopped— Voice recording stoppeddiscord:voice:uploaded— Voice segment uploadeddiscord:analysis:queue_status— Analysis queue status update
Shared Infrastructure
- Config: Zod-validated environment variables
- Logger: Winston logger with context support
- Database: Drizzle ORM with PostgreSQL
- Errors: Custom error classes with codes and status codes
- Utils: Retry logic with exponential backoff
No HTTP Server
- Discord Gateway service is event-driven only
- No Express, WebSocket, or HTTP routes
- All communication via Redis pub/sub
- Backend service consumes events and serves HTTP API
Initialization Flow
- Load environment config (Zod validation)
- Initialize database connection
- Run pending migrations
- Create Discord client with optimized cache settings
- Initialize Redis event broadcaster
- Register Discord event listeners (messageCapture, aiAnalyzer)
- Login to Discord
- Listen for graceful shutdown signals (SIGINT, SIGTERM)
Graceful Shutdown
On shutdown signal:
- Close database connection
- Disconnect from voice channels
- Close Redis connection
- Destroy Discord client
- Exit process
Dependencies
Core Discord:
- discord.js-selfbot-v13
- @discordjs/voice
- @discordjs/opus
Audio Processing:
- prism-media (Opus encoding/decoding)
- opusscript (Opus fallback)
- sharp (Image resizing)
Data & Config:
- drizzle-orm (ORM)
- pg (PostgreSQL driver)
- zod (Config validation)
- ioredis (Redis client)
Logging & Utilities:
- winston (Structured logging)
- p-retry (Retry logic)
- p-limit (Concurrency limiting)
- piscina (Worker pool)
Event Flow Example
Message Capture Flow
- Discord emits
messageCreateevent messageCapture.tslistener receives event- Extract metadata (user, channel, content, timestamp)
messageStore.tsinserts into databaseeventBroadcaster.messageCreated()publishes to Redis- Backend service subscribes to
discord:message:createdchannel - Backend processes and stores in its own database
Voice Recording Flow
voiceController.connect()joins voice channelrecorder.tssubscribes to user audio streams- For each speaking user:
- Create audio stream subscription
- Decode Opus packets to PCM
- Rotate OGG segments (5s default)
- Collect user metadata
- On silence (3s):
- Finalize segment
- Create metadata JSON
- Upload segment to storage
- Publish
discord:voice:uploadedevent
- Backend service receives event and indexes recording
No Breaking Changes
- Original
src/remains untouched for now - Discord Gateway is a new service in
services/discord-gateway/ - Can run alongside existing monolith during transition
- Backend service will consume Redis events
- Frontend continues to use Backend HTTP API