@riaskov/nevo-messaging 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +831 -0
  3. package/dist/common/base.client.d.ts +12 -0
  4. package/dist/common/base.client.js +50 -0
  5. package/dist/common/base.controller.d.ts +28 -0
  6. package/dist/common/base.controller.js +159 -0
  7. package/dist/common/bigint.utils.d.ts +7 -0
  8. package/dist/common/bigint.utils.js +50 -0
  9. package/dist/common/error-code.d.ts +3 -0
  10. package/dist/common/error-code.js +7 -0
  11. package/dist/common/error-messages.d.ts +1 -0
  12. package/dist/common/error-messages.js +7 -0
  13. package/dist/common/errors.d.ts +9 -0
  14. package/dist/common/errors.js +41 -0
  15. package/dist/common/index.d.ts +8 -0
  16. package/dist/common/index.js +24 -0
  17. package/dist/common/service-utils.d.ts +21 -0
  18. package/dist/common/service-utils.js +54 -0
  19. package/dist/common/signal-router.decorator.d.ts +9 -0
  20. package/dist/common/signal-router.decorator.js +67 -0
  21. package/dist/common/types.d.ts +72 -0
  22. package/dist/common/types.js +2 -0
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.js +20 -0
  25. package/dist/signal-router.utils.d.ts +28 -0
  26. package/dist/signal-router.utils.js +131 -0
  27. package/dist/signal.decorator.d.ts +15 -0
  28. package/dist/signal.decorator.js +67 -0
  29. package/dist/transports/index.d.ts +1 -0
  30. package/dist/transports/index.js +17 -0
  31. package/dist/transports/kafka/index.d.ts +5 -0
  32. package/dist/transports/kafka/index.js +21 -0
  33. package/dist/transports/kafka/kafka.client-base.d.ts +8 -0
  34. package/dist/transports/kafka/kafka.client-base.js +18 -0
  35. package/dist/transports/kafka/kafka.config.d.ts +16 -0
  36. package/dist/transports/kafka/kafka.config.js +210 -0
  37. package/dist/transports/kafka/kafka.signal-router.decorator.d.ts +3 -0
  38. package/dist/transports/kafka/kafka.signal-router.decorator.js +78 -0
  39. package/dist/transports/kafka/microservice.config.d.ts +10 -0
  40. package/dist/transports/kafka/microservice.config.js +46 -0
  41. package/dist/transports/kafka/nevo-kafka.client.d.ts +16 -0
  42. package/dist/transports/kafka/nevo-kafka.client.js +87 -0
  43. package/package.json +64 -0
package/README.md ADDED
@@ -0,0 +1,831 @@
1
+ # Nevo Messaging
2
+
3
+ A powerful microservices messaging framework for NestJS 11+ with Kafka 4+ transport, designed for building scalable distributed systems with type-safe inter-service communication.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Type-safe messaging** - Full TypeScript support with auto-completion
8
+ - 🔄 **Dual communication patterns** - Both request-response (query) and fire-and-forget (emit)
9
+ - 🎯 **Signal-based routing** - Declarative method mapping with `@Signal` decorator
10
+ - 📡 **Kafka transport** - Production-ready Apache Kafka integration
11
+ - 🔧 **Auto-configuration** - Automatic topic creation and client setup
12
+ - 🛡️ **Error handling** - Comprehensive error propagation and timeout management
13
+ - 📊 **BigInt support** - Native handling of large integers across services
14
+ - 🪝 **Lifecycle hooks** - Before/after message processing hooks
15
+ - 🔍 **Debug mode** - Built-in logging for development and troubleshooting
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @riaskov/nevo-messaging
21
+ ```
22
+
23
+ ### Peer Dependencies
24
+
25
+ ```bash
26
+ npm install @nestjs/common @nestjs/core @nestjs/microservices @nestjs/config @nestjs/platform-fastify kafkajs rxjs reflect-metadata
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### 1. Basic Service Setup
32
+
33
+ Create a simple microservice that responds to messages:
34
+
35
+ ```typescript
36
+ // user.service.ts
37
+ import { Injectable, Inject } from "@nestjs/common"
38
+ import { KafkaClientBase, NevoKafkaClient } from "@riaskov/nevo-messaging"
39
+
40
+ @Injectable()
41
+ export class UserService extends KafkaClientBase {
42
+ constructor(@Inject("NEVO_KAFKA_CLIENT") nevoClient: NevoKafkaClient) {
43
+ super(nevoClient)
44
+ }
45
+
46
+ async getById(id: bigint) {
47
+ return { id, name: "John Doe", email: "john@example.com" }
48
+ }
49
+
50
+ async create(userData: { name: string; email: string }) {
51
+ const newUser = { id: 123n, ...userData }
52
+ return newUser
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### 2. Signal Router Controller
58
+
59
+ Map service methods to external signals using the `@Signal` decorator:
60
+
61
+ ```typescript
62
+ // user.controller.ts
63
+ import { Controller, Inject } from "@nestjs/common"
64
+ import { KafkaSignalRouter, Signal } from "@riaskov/nevo-messaging"
65
+ import { UserService } from "./user.service"
66
+
67
+ @Controller()
68
+ @KafkaSignalRouter([UserService])
69
+ export class UserController {
70
+ constructor(@Inject(UserService) private readonly userService: UserService) {}
71
+
72
+ @Signal("user.getById", "getById", (data: any) => [data.id])
73
+ getUserById() {}
74
+
75
+ @Signal("user.create", "create", (data: any) => [data])
76
+ createUser() {}
77
+ }
78
+ ```
79
+
80
+ ### 3. Module Configuration
81
+
82
+ Configure the module with Kafka client:
83
+
84
+ ```typescript
85
+ // user.module.ts
86
+ import { Module } from "@nestjs/common"
87
+ import { ConfigModule } from "@nestjs/config"
88
+ import { createNevoKafkaClient } from "@riaskov/nevo-messaging"
89
+ import { UserController } from "./user.controller"
90
+ import { UserService } from "./user.service"
91
+
92
+ @Module({
93
+ imports: [ConfigModule],
94
+ controllers: [UserController],
95
+ providers: [
96
+ UserService,
97
+ createNevoKafkaClient(["COORDINATOR"], {
98
+ clientIdPrefix: "user"
99
+ })
100
+ ]
101
+ })
102
+ export class UserModule {}
103
+ ```
104
+
105
+ ### 4. Application Bootstrap
106
+
107
+ Start your microservice:
108
+
109
+ ```typescript
110
+ // main.ts
111
+ import { createKafkaMicroservice } from "@riaskov/nevo-messaging"
112
+ import { AppModule } from "./app.module"
113
+
114
+ createKafkaMicroservice({
115
+ microserviceName: "user",
116
+ module: AppModule,
117
+ port: 8086
118
+ }).then()
119
+ ```
120
+
121
+ ## Core Concepts
122
+
123
+ ### Signal Routing
124
+
125
+ The Signal Router pattern allows you to declaratively map external message patterns to internal service methods:
126
+
127
+ ```typescript
128
+ @Signal("external.signal.name", "internalMethodName", parameterTransformer?, resultTransformer?)
129
+ ```
130
+
131
+ ### Communication Patterns
132
+
133
+ #### Query Pattern (Request-Response)
134
+ Use for operations that need a response:
135
+
136
+ ```typescript
137
+ const user = await this.query("user", "user.getById", { id: 123n })
138
+ ```
139
+
140
+ #### Emit Pattern (Fire-and-Forget)
141
+ Use for events and notifications:
142
+
143
+ ```typescript
144
+ await this.emit("notifications", "user.created", { userId: 123n, email: "user@example.com" })
145
+ ```
146
+
147
+ ## Advanced Usage
148
+
149
+ ### Parameter Transformation
150
+
151
+ Transform incoming parameters before passing to service methods:
152
+
153
+ ```typescript
154
+ @Signal("user.update", "updateUser", (data: any) => [data.id, data.changes])
155
+ updateUser() {}
156
+
157
+ // Service method signature:
158
+ async updateUser(id: bigint, changes: Partial<User>) {
159
+ // Implementation
160
+ }
161
+ ```
162
+
163
+ ### Result Transformation
164
+
165
+ Transform service method results before sending response:
166
+
167
+ ```typescript
168
+ @Signal(
169
+ "user.getProfile",
170
+ "getById",
171
+ (data: any) => [data.id],
172
+ (user: User) => ({ ...user, password: undefined }) // Remove sensitive data
173
+ )
174
+ getProfile() {}
175
+ ```
176
+
177
+ ### Multiple Service Dependencies
178
+
179
+ Route signals to different services within the same controller:
180
+
181
+ ```typescript
182
+ @Controller()
183
+ @KafkaSignalRouter([UserService, ProfileService, NotificationService])
184
+ export class UserController {
185
+ constructor(
186
+ @Inject(UserService) private readonly userService: UserService,
187
+ @Inject(ProfileService) private readonly profileService: ProfileService,
188
+ @Inject(NotificationService) private readonly notificationService: NotificationService
189
+ ) {}
190
+
191
+ @Signal("user.create", "createUser", (data: any) => [data])
192
+ createUser() {}
193
+
194
+ @Signal("profile.update", "updateProfile", (data: any) => [data.userId, data.profile])
195
+ updateProfile() {}
196
+
197
+ @Signal("notification.send", "sendNotification", (data: any) => [data.userId, data.message])
198
+ sendNotification() {}
199
+ }
200
+ ```
201
+
202
+ ### Cross-Service Communication
203
+
204
+ Services can communicate with each other through the messaging layer:
205
+
206
+ ```typescript
207
+ @Injectable()
208
+ export class OrderService extends KafkaClientBase {
209
+ constructor(@Inject("NEVO_KAFKA_CLIENT") nevoClient: NevoKafkaClient) {
210
+ super(nevoClient)
211
+ }
212
+
213
+ async createOrder(orderData: CreateOrderDto) {
214
+ // Create the order
215
+ const order = await this.saveOrder(orderData)
216
+
217
+ // Query user service for user details
218
+ const user = await this.query("user", "user.getById", { id: orderData.userId })
219
+
220
+ // Query inventory service to reserve items
221
+ const reservation = await this.query("inventory", "item.reserve", {
222
+ items: orderData.items,
223
+ orderId: order.id
224
+ })
225
+
226
+ // Emit event to notification service
227
+ await this.emit("notifications", "order.created", {
228
+ orderId: order.id,
229
+ userId: user.id,
230
+ userEmail: user.email
231
+ })
232
+
233
+ return order
234
+ }
235
+ }
236
+ ```
237
+
238
+ ### Lifecycle Hooks
239
+
240
+ Add custom logic before and after message processing:
241
+
242
+ ```typescript
243
+ @KafkaSignalRouter([UserService], {
244
+ before: async (context) => {
245
+ console.log(`Processing ${context.method} for ${context.uuid}`)
246
+ // Validate request, log metrics, etc.
247
+ return context.params // Can modify parameters
248
+ },
249
+ after: async (context) => {
250
+ console.log(`Completed ${context.method} with result:`, context.result)
251
+ // Log metrics, audit trail, etc.
252
+ return context.response // Can modify response
253
+ },
254
+ debug: true
255
+ })
256
+ export class UserController {
257
+ // ...
258
+ }
259
+ ```
260
+
261
+ ### Error Handling
262
+
263
+ The framework provides comprehensive error handling:
264
+
265
+ ```typescript
266
+ import { MessagingError, ErrorCode } from "@riaskov/nevo-messaging"
267
+
268
+ @Injectable()
269
+ export class UserService extends KafkaClientBase {
270
+ async getById(id: bigint) {
271
+ const user = await this.findUser(id)
272
+
273
+ if (!user) {
274
+ throw new MessagingError(ErrorCode.UNKNOWN, {
275
+ message: "User not found",
276
+ userId: id
277
+ })
278
+ }
279
+
280
+ return user
281
+ }
282
+ }
283
+ ```
284
+
285
+ ## Configuration
286
+
287
+ ### Environment Variables
288
+
289
+ ```bash
290
+ # Kafka Configuration
291
+ KAFKA_HOST=localhost
292
+ KAFKA_PORT=9092
293
+ NODE_ENV=production
294
+ ```
295
+
296
+ ### Kafka Client Options
297
+
298
+ ```typescript
299
+ createNevoKafkaClient(["USER", "INVENTORY", "NOTIFICATIONS"], {
300
+ clientIdPrefix: "order-service",
301
+ groupIdPrefix: "order-consumer",
302
+ sessionTimeout: 30000,
303
+ allowAutoTopicCreation: true,
304
+ retryAttempts: 5,
305
+ brokerRetryTimeout: 2000,
306
+ timeoutMs: 25000,
307
+ debug: false
308
+ })
309
+ ```
310
+
311
+ ### Microservice Startup Options
312
+
313
+ ```typescript
314
+ createKafkaMicroservice({
315
+ microserviceName: "user",
316
+ module: AppModule,
317
+ port: 8086,
318
+ host: "0.0.0.0",
319
+ debug: true,
320
+ onInit: async (app) => {
321
+ // Custom initialization logic
322
+ await app.get(DatabaseService).runMigrations()
323
+ }
324
+ })
325
+ ```
326
+
327
+ ## BigInt Support
328
+
329
+ The framework automatically handles BigInt serialization across service boundaries:
330
+
331
+ ```typescript
332
+ // Service returns BigInt
333
+ async getUserId(): Promise<bigint> {
334
+ return 9007199254740991n // Large integer
335
+ }
336
+
337
+ // Automatically serialized as "9007199254740991n"
338
+ // Automatically deserialized back to BigInt on the receiving end
339
+ ```
340
+
341
+ ## Architecture Patterns
342
+
343
+ ### Event Sourcing Pattern
344
+
345
+ Use events to maintain state consistency across services:
346
+
347
+ ```typescript
348
+ @Injectable()
349
+ export class OrderService extends KafkaClientBase {
350
+ async createOrder(orderData: CreateOrderDto) {
351
+ const order = await this.saveOrder(orderData)
352
+
353
+ // Emit domain events
354
+ await Promise.all([
355
+ this.emit("events", "order.created", {
356
+ orderId: order.id,
357
+ userId: order.userId,
358
+ timestamp: new Date(),
359
+ aggregateVersion: 1
360
+ }),
361
+ this.emit("events", "inventory.reserved", {
362
+ orderId: order.id,
363
+ items: order.items,
364
+ timestamp: new Date()
365
+ })
366
+ ])
367
+
368
+ return order
369
+ }
370
+ }
371
+ ```
372
+
373
+ ### CQRS Pattern
374
+
375
+ Separate command and query responsibilities:
376
+
377
+ ```typescript
378
+ // Command Service
379
+ @Injectable()
380
+ export class UserCommandService extends KafkaClientBase {
381
+ async createUser(userData: CreateUserDto) {
382
+ const user = await this.repository.save(userData)
383
+
384
+ // Emit event for read model updates
385
+ await this.emit("events", "user.created", {
386
+ userId: user.id,
387
+ email: user.email,
388
+ timestamp: new Date()
389
+ })
390
+
391
+ return user
392
+ }
393
+ }
394
+
395
+ // Query Service
396
+ @Injectable()
397
+ export class UserQueryService extends KafkaClientBase {
398
+ async getUserProfile(userId: bigint) {
399
+ // Optimized read model
400
+ return this.readRepository.findUserProfile(userId)
401
+ }
402
+ }
403
+ ```
404
+
405
+ ## Advanced Configuration
406
+
407
+ ### Custom Message Extractors
408
+
409
+ For complex message formats:
410
+
411
+ ```typescript
412
+ export function createCustomSignalRouter(serviceType: Type<any>[], options?: SignalRouterOptions) {
413
+ return createSignalRouterDecorator(
414
+ serviceType,
415
+ options,
416
+ (data) => {
417
+ // Custom message extraction logic
418
+ const envelope = parseWithBigInt(data.value)
419
+ return {
420
+ method: envelope.command.action,
421
+ params: envelope.payload,
422
+ uuid: envelope.metadata.correlationId
423
+ }
424
+ },
425
+ (target, eventPattern, handlerName) => {
426
+ // Custom handler registration
427
+ MessagePattern(eventPattern)(target.prototype, handlerName,
428
+ Object.getOwnPropertyDescriptor(target.prototype, handlerName)!)
429
+ }
430
+ )
431
+ }
432
+ ```
433
+
434
+ ### Distributed Tracing
435
+
436
+ Implement correlation IDs for request tracing:
437
+
438
+ ```typescript
439
+ @KafkaSignalRouter([UserService], {
440
+ before: async (context) => {
441
+ // Inject correlation ID
442
+ const correlationId = context.uuid
443
+ context.params.correlationId = correlationId
444
+
445
+ console.log(`[${correlationId}] Starting ${context.method}`)
446
+ return context.params
447
+ },
448
+ after: async (context) => {
449
+ const correlationId = context.params.correlationId
450
+ console.log(`[${correlationId}] Completed ${context.method}`)
451
+ return context.response
452
+ }
453
+ })
454
+ export class UserController {
455
+ // ...
456
+ }
457
+ ```
458
+
459
+ ### Retry Policies
460
+
461
+ Configure retry behavior for failed operations:
462
+
463
+ ```typescript
464
+ @Injectable()
465
+ export class ResilientService extends KafkaClientBase {
466
+ async performOperation(data: any) {
467
+ const maxRetries = 3
468
+ let attempt = 0
469
+
470
+ while (attempt < maxRetries) {
471
+ try {
472
+ return await this.query("external", "risky.operation", data)
473
+ } catch (error) {
474
+ attempt++
475
+ if (attempt >= maxRetries) throw error
476
+
477
+ await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
478
+ }
479
+ }
480
+ }
481
+ }
482
+ ```
483
+
484
+ ## Performance Optimization
485
+
486
+ ### Batch Operations
487
+
488
+ Process multiple operations efficiently:
489
+
490
+ ```typescript
491
+ @Injectable()
492
+ export class BatchUserService extends KafkaClientBase {
493
+ async processBatch(userIds: bigint[]) {
494
+ // Process in chunks to avoid overwhelming downstream services
495
+ const chunkSize = 10
496
+ const results = []
497
+
498
+ for (let i = 0; i < userIds.length; i += chunkSize) {
499
+ const chunk = userIds.slice(i, i + chunkSize)
500
+ const chunkResults = await Promise.all(
501
+ chunk.map(id => this.query("user", "user.getById", { id }))
502
+ )
503
+ results.push(...chunkResults)
504
+ }
505
+
506
+ return results
507
+ }
508
+ }
509
+ ```
510
+
511
+ ### Caching Layer
512
+
513
+ Implement service-level caching:
514
+
515
+ ```typescript
516
+ @Injectable()
517
+ export class CachedUserService extends KafkaClientBase {
518
+ private cache = new Map<string, any>()
519
+ private readonly cacheTimeout = 300000 // 5 minutes
520
+
521
+ async getCachedUser(id: bigint) {
522
+ const cacheKey = `user:${id}`
523
+ const cached = this.cache.get(cacheKey)
524
+
525
+ if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
526
+ return cached.data
527
+ }
528
+
529
+ const user = await this.query("user", "user.getById", { id })
530
+ this.cache.set(cacheKey, { data: user, timestamp: Date.now() })
531
+
532
+ return user
533
+ }
534
+ }
535
+ ```
536
+
537
+ ## Monitoring and Observability
538
+
539
+ ### Health Checks
540
+
541
+ Implement service health monitoring:
542
+
543
+ ```typescript
544
+ @Injectable()
545
+ export class HealthService extends KafkaClientBase {
546
+ async checkServiceHealth() {
547
+ const services = this.getAvailableServices()
548
+ const healthChecks = await Promise.allSettled(
549
+ services.map(async (service) => {
550
+ try {
551
+ await this.query(service, "health.check", {})
552
+ return { service, status: "healthy" }
553
+ } catch (error) {
554
+ return { service, status: "unhealthy", error: error.message }
555
+ }
556
+ })
557
+ )
558
+
559
+ return healthChecks.map(result =>
560
+ result.status === "fulfilled" ? result.value : result.reason
561
+ )
562
+ }
563
+ }
564
+ ```
565
+
566
+ ### Metrics Collection
567
+
568
+ Track message processing metrics:
569
+
570
+ ```typescript
571
+ @KafkaSignalRouter([MetricsService], {
572
+ before: async (context) => {
573
+ context.startTime = Date.now()
574
+ return context.params
575
+ },
576
+ after: async (context) => {
577
+ const duration = Date.now() - context.startTime
578
+
579
+ await this.emit("metrics", "message.processed", {
580
+ service: context.serviceName,
581
+ method: context.method,
582
+ duration,
583
+ success: context.response.params.result !== "error"
584
+ })
585
+
586
+ return context.response
587
+ }
588
+ })
589
+ export class MetricsController {
590
+ // ...
591
+ }
592
+ ```
593
+
594
+ ## Security
595
+
596
+ ### Message Validation
597
+
598
+ Implement input validation:
599
+
600
+ ```typescript
601
+ import { IsString, IsEmail, validate } from "class-validator"
602
+
603
+ class CreateUserDto {
604
+ @IsString()
605
+ name: string
606
+
607
+ @IsEmail()
608
+ email: string
609
+ }
610
+
611
+ @Injectable()
612
+ export class SecureUserService extends KafkaClientBase {
613
+ async createUser(userData: any) {
614
+ const dto = Object.assign(new CreateUserDto(), userData)
615
+ const errors = await validate(dto)
616
+
617
+ if (errors.length > 0) {
618
+ throw new MessagingError(ErrorCode.UNKNOWN, {
619
+ message: "Validation failed",
620
+ errors: errors.map(e => e.constraints)
621
+ })
622
+ }
623
+
624
+ return this.repository.save(dto)
625
+ }
626
+ }
627
+ ```
628
+
629
+ ### Authentication Context
630
+
631
+ Pass authentication context between services:
632
+
633
+ ```typescript
634
+ @KafkaSignalRouter([UserService], {
635
+ before: async (context) => {
636
+ // Extract and validate auth token
637
+ const authHeader = context.rawData.headers?.authorization
638
+ const user = await this.validateToken(authHeader)
639
+
640
+ return {
641
+ ...context.params,
642
+ authContext: { userId: user.id, roles: user.roles }
643
+ }
644
+ }
645
+ })
646
+ export class SecureUserController {
647
+ // ...
648
+ }
649
+ ```
650
+
651
+ ## Production Deployment
652
+
653
+ ### Docker Compose Setup
654
+
655
+ ```yaml
656
+ services:
657
+ kafka:
658
+ image: apache/kafka:4.0.0
659
+ environment:
660
+ - KAFKA_PROCESS_ROLES=broker,controller
661
+ - KAFKA_NODE_ID=1
662
+ - KAFKA_CONTROLLER_QUORUM_VOTERS=1@kafka:9093
663
+ - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
664
+ - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092
665
+ - KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
666
+
667
+ user-service:
668
+ build: ./user-service
669
+ environment:
670
+ - KAFKA_HOST=kafka
671
+ - KAFKA_PORT=9092
672
+ depends_on:
673
+ - kafka
674
+
675
+ order-service:
676
+ build: ./order-service
677
+ environment:
678
+ - KAFKA_HOST=kafka
679
+ - KAFKA_PORT=9092
680
+ depends_on:
681
+ - kafka
682
+ ```
683
+
684
+ ### Scaling Considerations
685
+
686
+ Configure partition strategy for high throughput:
687
+
688
+ ```typescript
689
+ createNevoKafkaClient(["HIGH_VOLUME_SERVICE"], {
690
+ clientIdPrefix: "processor",
691
+ partitionStrategy: "round-robin",
692
+ maxInFlightRequests: 5,
693
+ batchSize: 100,
694
+ lingerMs: 10
695
+ })
696
+ ```
697
+
698
+ ## API Reference
699
+
700
+ ### Decorators
701
+
702
+ #### `@Signal(signalName, methodName?, paramTransformer?, resultTransformer?)`
703
+
704
+ Maps external signals to service methods.
705
+
706
+ **Parameters:**
707
+ - `signalName` (string): External signal identifier
708
+ - `methodName` (string, optional): Service method name (defaults to signalName)
709
+ - `paramTransformer` (function, optional): Transform incoming parameters
710
+ - `resultTransformer` (function, optional): Transform outgoing results
711
+
712
+ #### `@KafkaSignalRouter(serviceTypes, options?)`
713
+
714
+ Class decorator for signal routing setup.
715
+
716
+ **Parameters:**
717
+ - `serviceTypes` (Type<any> | Type<any>[]): Service classes to route to
718
+ - `options` (object, optional): Configuration options
719
+
720
+ ### Classes
721
+
722
+ #### `KafkaClientBase`
723
+
724
+ Base class for services that need to communicate with other microservices.
725
+
726
+ **Methods:**
727
+ - `query<T>(serviceName, method, params): Promise<T>` - Request-response communication
728
+ - `emit(serviceName, method, params): Promise<void>` - Fire-and-forget communication
729
+ - `getAvailableServices(): string[]` - List registered services
730
+
731
+ #### `NevoKafkaClient`
732
+
733
+ Universal Kafka client for multi-service communication.
734
+
735
+ **Methods:**
736
+ - `query<T>(serviceName, method, params): Promise<T>` - Send query to service
737
+ - `emit(serviceName, method, params): Promise<void>` - Emit event to service
738
+ - `getAvailableServices(): string[]` - Get list of available services
739
+
740
+ ### Functions
741
+
742
+ #### `createNevoKafkaClient(serviceNames, options)`
743
+
744
+ Factory function for creating Kafka client providers.
745
+
746
+ #### `createKafkaMicroservice(options)`
747
+
748
+ Bootstrap function for starting NestJS microservices with Kafka transport.
749
+
750
+ ## Troubleshooting
751
+
752
+ ### Common Issues
753
+
754
+ **Topic Creation Failures**
755
+ ```bash
756
+ # Ensure Kafka is running and accessible
757
+ docker-compose up kafka
758
+
759
+ # Check topic creation logs
760
+ docker-compose logs kafka
761
+ ```
762
+
763
+ **Connection Timeouts**
764
+ ```typescript
765
+ // Increase timeouts for slow networks
766
+ createNevoKafkaClient(["USER"], {
767
+ clientIdPrefix: "app",
768
+ timeoutMs: 30000,
769
+ sessionTimeout: 45000
770
+ })
771
+ ```
772
+
773
+ **Serialization Errors**
774
+ ```typescript
775
+ // Enable debug mode to see message payloads
776
+ @KafkaSignalRouter([UserService], { debug: true })
777
+ ```
778
+
779
+ ### Debug Mode
780
+
781
+ Enable comprehensive logging:
782
+
783
+ ```bash
784
+ NODE_ENV=development
785
+ ```
786
+
787
+ ```typescript
788
+ createKafkaMicroservice({
789
+ microserviceName: "user",
790
+ module: AppModule,
791
+ debug: true
792
+ })
793
+ ```
794
+
795
+ ## Migration Guide
796
+
797
+ ### From Other Messaging Libraries
798
+
799
+ If migrating from other microservice messaging solutions:
800
+
801
+ 1. **Replace message handlers** with `@Signal` decorators
802
+ 2. **Update service injection** to use `KafkaClientBase`
803
+ 3. **Configure Kafka clients** with `createNevoKafkaClient`
804
+ 4. **Update message patterns** to use signal names
805
+
806
+ ### Version Compatibility
807
+
808
+ - **Node.js**: ≥24.0.0
809
+ - **NestJS**: ≥11.1.0
810
+ - **Kafka**: ≥2.8.0 (≥4.0.0 is recommended)
811
+
812
+ ## Contributing
813
+
814
+ 1. Fork the repository
815
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
816
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
817
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
818
+ 5. Open a Pull Request
819
+
820
+ ## License
821
+
822
+ MIT License - see [LICENSE](LICENSE) file for details.
823
+
824
+ ## Support
825
+
826
+ - GitHub Issues: [Report bugs and request features](https://github.com/ARyaskov/nevo-messaging/issues)
827
+ - Documentation: This README and inline code documentation
828
+ - Examples: Check the `examples/` directory for complete working examples
829
+
830
+ ## Aux
831
+ There are many anys in core code - the simple temporary solution for changeable Nest.js microservices API.