Queue Types Refactoring
· 4 min read
Problem Statement
The current job queue system has several issues:
- Job names and data types are not properly typed
- Job data types are not enforced at compile time
- Queue and job names are scattered across different files
- No clear relationship between queues and their valid job types
- Type safety is not enforced when adding jobs to queues
Solution Architecture
1. Module-Level Types
Each module that uses queues will have its own types/queue.types.ts file:
// apps/mercury/src/atlas/types/queue.types.ts
export enum AtlasQueueName {
ATLAS = 'atlas',
TA_AVAILABILITY = 'ta_availability',
}
export enum AtlasJobName {
CASSANDRA_SAMPLE_ANALYSIS = 'cassandra_sample_analysis',
CASSANDRA_MARKET_ANALYSIS = 'cassandra_market_analysis',
// ...
}
// Job data type definitions
export interface CassandraSampleAnalysisData {
symbol: string;
// ...
}
export interface CassandraMarketAnalysisData {
markets: string[];
// ...
}
// Map job names to their data types
export interface AtlasJobDataMap {
[AtlasJobName.CASSANDRA_SAMPLE_ANALYSIS]: CassandraSampleAnalysisData;
[AtlasJobName.CASSANDRA_MARKET_ANALYSIS]: CassandraMarketAnalysisData;
}
// Define valid jobs for each queue
export const AtlasQueueJobs = {
[AtlasQueueName.ATLAS]: [
AtlasJobName.CASSANDRA_SAMPLE_ANALYSIS,
AtlasJobName.CASSANDRA_MARKET_ANALYSIS,
],
[AtlasQueueName.TA_AVAILABILITY]: [
// ...
],
} as const;
// Type to get valid jobs for a queue
export type AtlasQueueJobMap = {
[Q in keyof typeof AtlasQueueJobs]: (typeof AtlasQueueJobs)[Q][number];
};
2. Root Types
The root types file will combine all module-specific types:
// apps/mercury/src/types/queue.types.ts
import {
AtlasQueueName,
AtlasJobName,
AtlasJobDataMap,
AtlasQueueJobMap
} from '../atlas/types/queue.types';
import {
DikeQueueName,
DikeJobName,
DikeJobDataMap,
DikeQueueJobMap
} from '../dike/types/queue.types';
// ... other imports
// Combine all queue names
export type QueueName = AtlasQueueName | DikeQueueName | /* ... */;
// Combine all job names
export type JobName = AtlasJobName | DikeJobName | /* ... */;
// Map queues to their jobs
export type QueueJobMap = AtlasQueueJobMap & DikeQueueJobMap & /* ... */;
// Map jobs to their data
export type JobDataMap = AtlasJobDataMap & DikeJobDataMap & /* ... */;
// Helper to get job data type
export type JobData<
Q extends QueueName,
J extends JobName
> = J extends keyof JobDataMap ? JobDataMap[J] : never;
// Helper to get queue type
export type Queue<Q extends QueueName> = BullMQ.Queue<
JobData<Q, QueueJobMap[Q]>,
any,
QueueJobMap[Q]
>;
3. Usage in Consumers
Example of a type-safe consumer:
@Injectable()
@Processor(AtlasQueueName.ATLAS)
export class AtlasConsumer extends WorkerHost {
constructor(
@InjectQueue(AtlasQueueName.ATLAS)
private readonly queue: Queue<AtlasQueueName.ATLAS>,
) {
super();
}
async process(job: Job<JobData<AtlasQueueName.ATLAS, AtlasJobName>>) {
switch (job.name) {
case AtlasJobName.CASSANDRA_SAMPLE_ANALYSIS:
// TypeScript knows job.data is CassandraSampleAnalysisData
return this.handleCassandraSampleAnalysis(job.data);
case AtlasJobName.CASSANDRA_MARKET_ANALYSIS:
// TypeScript knows job.data is CassandraMarketAnalysisData
return this.handleCassandraMarketAnalysis(job.data);
}
}
}
4. Usage in Producers
Example of a type-safe producer:
@Injectable()
export class AtlasProducer {
constructor(
@InjectQueue(AtlasQueueName.ATLAS)
private readonly queue: Queue<AtlasQueueName.ATLAS>,
) {}
async addCassandraSampleAnalysis(
data: JobData<AtlasQueueName.ATLAS, AtlasJobName.CASSANDRA_SAMPLE_ANALYSIS>,
) {
return this.queue.add(AtlasJobName.CASSANDRA_SAMPLE_ANALYSIS, data);
}
}
Implementation Plan
-
Discovery Phase
- Find all queue consumers using grep:
@Processor - Find all job name usages:
JobName. - Find all queue name usages:
QueueName. - Document all job data types currently in use
- Find all queue consumers using grep:
-
Per-Module Implementation
- Create module's
types/queue.types.ts - Move queue and job names to module-specific enums
- Create job data interfaces
- Create job data map
- Define valid jobs for each queue
- Update imports in module files
- Update consumer to use new types
- Update producer to use new types
- Verify type safety with
tsc
- Create module's
-
Root Types Integration
- Create/update root types file
- Import and combine all module types
- Create helper types
- Update cross-module references
-
Validation
- Run TypeScript compiler
- Check for any implicit
anytypes - Verify job data types are enforced
- Test queue operations
Benefits
-
Type Safety
- Queue names are typed
- Job names are typed and queue-specific
- Job data is typed
- TypeScript enforces correct job data structure
-
Developer Experience
- IDE autocompletion for job names and data
- Refactoring support
- Clear relationship between queues and jobs
- Self-documenting code
-
Maintainability
- Modular type definitions
- Easy to add new queues and jobs
- Type errors caught at compile time
- Clear separation of concerns
Migration Strategy
- Start with one module as proof of concept (e.g., Atlas)
- Get team feedback on the approach
- Iterate on the design if needed
- Roll out to other modules
- Update root types incrementally
- Full type safety check after all modules are migrated
Rollback Plan
Since this is a TypeScript-only change that doesn't affect runtime behavior:
- Keep old type definitions until migration is complete
- Can roll back individual modules if issues are found
- No impact on production runtime
Next Steps
- Choose first module for proof of concept
- Create module types
- Update module consumers/producers
- Review with team
- Plan full rollout
Progress Tracking
Atlas Module (✓)
- ✓
atlas.consumer.ts - ✓
atlas.producer.ts - ✓
atlas.module.ts - ✓
types/queue.types.ts
Artifacts Module (✓)
- ✓
artifact-update-github.consumer.ts - ✓
artifact-update-tg.consumer.ts - ✓
artifacts.module.ts - ✓
types/queue.types.ts
Minerva Module (✓)
- ✓
ta.consumer.ts - ✓
ta.producer.ts - ✓
ta.module.ts - ✓
types/queue.types.ts
Morpheus Module (✓)
- ✓
shadow-portfolio.consumer.ts - ✓
shadow-portfolio.producer.ts - ✓
shadow-portfolio.module.ts - ✓
types/queue.types.ts
Dike Module (✓)
- ✓
tournament.consumer.ts - ✓
tournament.producer.ts - ✓
tournament.module.ts - ✓
types/queue.types.ts
Thoth Module (Pending)
- ⏳
rag.consumer.ts - ⏳
rag.producer.ts - ⏳
rag.module.ts - ⏳
types/queue.types.ts
Hermes Module (Pending)
- ⏳
telegram.consumer.ts - ⏳
telegram.producer.ts - ⏳
telegram.module.ts - ⏳
types/queue.types.ts
Core Types (✓)
- ✓
queue.types.ts(updated with re-exports) - ✓
queue.config.ts(using ENV_DEFAULTS) - ✓
env.defaults.ts(with critical safety warning)
dev@redmax:~/ton-arcana$ echo "Find all job name usages:" && find apps -type f -name "*.ts" -exec grep -l "JobName\." {} \; && echo -e "\nFind all queue name usages:" && find apps -type f -name "*.ts" -exec grep -l echo "Find all job name usages:" && find apps -type -name "*.ts" -exec -l "JobName\." {} && echo -e "\nFind all queue name usages:" && find apps -type -name "*.ts" -exec -l "QueueName\." {}
Find all job name usages:
apps/arcana-bot/src/image/image.consumer.ts
apps/arcana-bot/src/teachings/teaching.consumer.ts
apps/arcana-bot/src/teachings/teaching.service.ts
apps/arcana-bot/src/telegram/telegram.consumer.ts
apps/arcana-bot/src/telegram/telegram.service.ts
apps/arcana-bot/src/types/queue.types.ts
apps/arcana-bot/src/worker/tarot.consumer.ts
apps/arcana-bot/src/worker/worker.service.ts
apps/mercury/src/artifacts/artifacts.service.ts
apps/mercury/src/atlas/orchestrators/ta-availability.orchestrator.ts
apps/mercury/src/atlas/orchestrators/daily-report.orchestrator.ts
apps/mercury/src/atlas/orchestrators/tournament.orchestrator.ts
apps/mercury/src/atlas/atlas.module.ts
apps/mercury/src/telegram/handlers/admin/daily-report.handler.ts
apps/mercury/src/telegram/handlers/admin/ta-availability.handler.ts
apps/mercury/src/telegram/telegram.consumer.ts
apps/mercury/src/telegram/telegram.service.ts
apps/mercury/src/apollo/report/report.consumer.ts
apps/mercury/src/dike/tournament.service.ts
apps/mercury/src/dike/tournament.consumer.ts
apps/mercury/src/dike/tournament.producer.ts
Find all queue name usages:
apps/arcana-bot/src/image/image.consumer.ts
apps/arcana-bot/src/image/image.module.ts
apps/arcana-bot/src/teachings/teaching.consumer.ts
apps/arcana-bot/src/teachings/teaching.module.ts
apps/arcana-bot/src/teachings/teaching.service.ts
apps/arcana-bot/src/telegram/telegram.consumer.ts
apps/arcana-bot/src/telegram/telegram.service.ts
apps/arcana-bot/src/types/queue.types.ts
apps/arcana-bot/src/worker/tarot.consumer.ts
apps/arcana-bot/src/worker/worker.module.ts
apps/arcana-bot/src/worker/worker.service.ts
apps/mercury/src/admin/admin.consumer.ts
apps/mercury/src/admin/admin.module.ts
apps/mercury/src/artifacts/artifact-update-github.consumer.ts
apps/mercury/src/artifacts/artifact-update-tg.consumer.ts
apps/mercury/src/artifacts/artifacts.module.ts
apps/mercury/src/artifacts/artifacts.service.ts
apps/mercury/src/atlas/orchestrators/cassandra.orchestrator.ts
apps/mercury/src/atlas/orchestrators/ta-availability.orchestrator.ts
apps/mercury/src/atlas/orchestrators/daily-report.orchestrator.ts
apps/mercury/src/atlas/orchestrators/tournament.orchestrator.ts
apps/mercury/src/atlas/atlas.consumer.ts
apps/mercury/src/atlas/consumers/ta-availability.consumer.ts
apps/mercury/src/atlas/atlas.module.ts
apps/mercury/src/telegram/telegram.consumer.ts
apps/mercury/src/telegram/telegram.module.ts
apps/mercury/src/telegram/telegram.service.ts
apps/mercury/src/types/queue.types.ts
apps/mercury/src/cassandra/cassandra.module.ts
apps/mercury/src/apollo/report/report.consumer.ts
apps/mercury/src/apollo/report/report.module.ts
apps/mercury/src/dike/tournament.service.ts
apps/mercury/src/dike/dike.module.ts
apps/mercury/src/dike/tournament.consumer.ts
apps/mercury/src/dike/tournament.producer.ts
apps/mercury/src/minerva/ta-availability.consumer.ts
apps/mercury/src/minerva/minerva.module.ts
apps/mercury/src/morpheus/shadow-portfolio.consumer.ts
apps/mercury/src/morpheus/shadow-portfolio.producer.ts
apps/mercury/src/morpheus/shadow-portfolio.service.spec.ts
apps/mercury/src/morpheus/morpheus.module.ts
apps/mercury/src/morpheus/shadow-portfolio.module.ts
apps/mercury/src/tyche/test/market-comparison.service.e2e-spec.ts
apps/mercury/src/tyche/test/market-ranking.service.e2e-spec.ts
apps/mercury/src/tyche/tyche.module.ts
apps/mercury/src/tyche/market-ranking.service.ts
dev@redmax:~/ton-arcana$