Skip to content

Conversation

@sameerkrdev
Copy link
Owner

@sameerkrdev sameerkrdev commented Nov 12, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added request validation for order placement with comprehensive field checks
    • Implemented Kafka-based event handling for order persistence and scaling
  • Refactor

    • User registration now uses username field instead of name
    • Docker infrastructure split into modular, service-specific components
    • Order service separated into gRPC producer and Kafka consumer responsibilities
  • Chores

    • Updated environment configuration across services
    • Added infrastructure automation for Kafka topic management

@coderabbitai
Copy link

coderabbitai bot commented Nov 12, 2025

Walkthrough

The PR introduces Kafka-based event-driven order processing by creating a new order-worker service that consumes Kafka messages, adding a shared Kafka client package with producer/consumer abstractions, refactoring the order service to emit events instead of persisting directly, implementing request validation middleware, and reorganizing Docker infrastructure into modular compose files.

Changes

Cohort / File(s) Summary
Environment Configuration
.env.example, apps/api-gateway/.env.example, apps/order-service/.env.example, apps/order-worker-service/.env.example
Added .env.example template files across root and services defining database, Kafka, and gRPC configuration variables.
API Gateway Configuration & Validation
apps/api-gateway/src/config/dotenv.ts, apps/api-gateway/src/middlewares/zod.validator.middleware.ts
Added ORDER_SERVICE_GRPC_URL environment variable support; introduced Zod-based request validation middleware for standardized error handling and schema parsing.
API Gateway Routes & Controllers
apps/api-gateway/src/routers/order.route.ts, apps/api-gateway/src/controllers/order.controller.ts, apps/api-gateway/src/controllers/user.controller.ts
Updated order route to use environment-based gRPC URL and apply validation middleware; modified order controller to assert non-null price; updated user controller to use username instead of name.
API Gateway Type System
apps/api-gateway/src/types/index.ts, apps/api-gateway/package.json
Replaced internal Order type with PlaceOrder from validator package; reordered @repo/proto-defs dependency.
Order Service Refactor
apps/order-service/src/grpc.server.ts, apps/order-service/src/index.ts, apps/order-service/src/controllers/order.controller.ts, apps/order-service/src/services/order.service.ts, apps/order-service/src/config/dotenv.ts, apps/order-service/package.json
Extracted gRPC server into separate module; refactored controller to emit Kafka events via KafkaClient instead of direct persistence; removed OrderService class; added Kafka and environment configuration.
Order Worker Service (New)
apps/order-worker-service/... (package.json, tsconfig.json, eslint.config.mjs, nodemon.json, src/index.ts, src/config/dotenv.ts, src/kafka.consumer.ts, src/controllers/order.controller.ts, src/constants.ts, README.md)
New microservice consuming Kafka order events and persisting to database via OrderRepository; includes Kafka consumer, environment config, and controller for persistence logic.
Kafka Client Package (New)
packages/kafka-client/... (package.json, tsconfig.json, eslint.config.mjs, src/index.ts, src/constants.ts, Readme.md)
New shared package implementing KafkaClient class with producer/consumer abstractions, Zod schema validation, structured logging, and topic/event constants.
Validator Package (New)
packages/validator/... (package.json, eslint.config.mjs, src/Order.ts, src/index.ts, tsconfig.json)
New shared validation package defining PlaceOrderValidator Zod schema with conditional price requirement for non-MARKET orders.
Type & Prisma Packages
packages/types/... (index.d.ts, order.d.ts, user.d.ts, package.json, tsconfig.json), packages/prisma/src/index.ts, packages/prisma/package.json
Updated User type to use username instead of name; added OrderSideKeys and OrderTypeKeys type aliases; expanded Prisma type exports; added proto-defs path mapping and db:studio script.
Docker Infrastructure
infra/docker/docker-compose.yml, infra/docker/clickhouse/docker-compose.yml, infra/docker/db/docker-compose.yml, infra/docker/kafka/docker-compose.yml, infra/docker/kafka/create-topics.sh, infra/docker/.env.example
Restructured monolithic docker-compose into modular per-service files (ClickHouse, PostgreSQL/Redis, Kafka cluster); removed root .env.example; added automated topic creation script.
Orchestration & Build Configuration
package.json, turbo.json
Refactored infra commands into modular per-service scripts (db/kafka/clickhouse); added database-related Turbo tasks (db:studio, db:generate, db:migrate, db:deploy); added db:studio Turbo task.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant API as api-gateway<br/>(gRPC client)
    participant Validator as Zod Validator<br/>Middleware
    participant OrderCtrl as Order<br/>Controller
    participant KafkaClient
    participant Kafka as Kafka Broker<br/>(orders topic)
    participant Worker as order-worker-service<br/>(consumer)
    participant Prisma as Database<br/>(persistence)

    Client->>API: POST /orders<br/>(PlaceOrder payload)
    API->>Validator: Validate body,<br/>query, params
    alt Validation Failure
        Validator-->>API: 400 Error
        API-->>Client: Error Response
    else Validation Success
        Validator->>OrderCtrl: Request forwarded
        OrderCtrl->>KafkaClient: sendMessage(topic:"orders",<br/>eventType:"create")
        KafkaClient->>Kafka: Emit KafkaMessage
        Kafka-->>OrderCtrl: Ack
        OrderCtrl-->>Client: GrpcCreateOrderResponse
    end
    
    rect rgb(220, 240, 255)
    note over Kafka,Prisma: Async Event Processing
    Kafka->>Worker: Consume message<br/>(eventType:"create")
    Worker->>Prisma: OrderRepository.create(<br/>status:PENDING)
    Prisma-->>Worker: Order persisted
    Worker->>Worker: Log success
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • apps/order-service refactor: The shift from direct persistence (OrderService) to Kafka-based event emission requires verification that the gRPC controller correctly generates orderId and formats KafkaMessage payloads, and that the new GrpcServer class properly initializes and manages lifecycle.
  • Kafka client implementation: Review KafkaClient sendMessage/subscribe logic, including schema validation paths, error handling, and offset commit retry behavior to ensure robustness in production.
  • Order worker service Kafka consumer: Verify KafkaConsumer.startConsuming correctly handles eventType routing, error scenarios, and graceful shutdown, especially order persistence dependency injection.
  • Validation middleware and PlaceOrderValidator: Confirm the conditional price requirement (mandatory for LIMIT/STOP orders) is correctly enforced and that middleware integrates properly with all route handlers.
  • Docker Compose refactoring: Ensure network (nerve-exchange-network), volumes, and inter-service health checks are correctly defined across modular files and that the kafka-init topic creation logic completes before brokers are considered ready.

Possibly related PRs

  • PR #8: Builds on the order gRPC integration and proto-client modifications established in this PR.
  • PR #7: Complements the Prisma package surface expansion and database layer enhancements for Order/Trade models introduced here.
  • PR #2: Related through shared API Gateway environment configuration and dotenv setup patterns.

Poem

🐰 A Kafka-driven dream takes flight,
Events hop through topics bright,
Workers listen, persist with care,
Orders processed everywhere!
From API to persistence' door,
Events make the system soar. 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feat/kafka' is vague and does not clearly convey the scope or purpose of the substantial changes. The PR introduces Kafka integration, new services, Docker infrastructure, validation middleware, and architectural refactoring across multiple apps and packages, but the title provides no meaningful context about these changes. Improve the title to be more descriptive of the main changes, such as 'Integrate Kafka event streaming and split order service architecture' or 'Add Kafka-based order processing with worker service separation'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/kafka

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 21

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/order-service/src/index.ts (1)

10-20: Kafka producer connection not closed during graceful shutdown.

The GrpcServer.shutdown() method only shuts down the gRPC server but leaves the Kafka producer connection open. Since OrderServerController calls kafkaClient.sendMessage() during request handling, which internally connects the producer via getProducer(), this connection persists after shutdown and prevents clean termination.

Update apps/order-service/src/grpc.server.ts to disconnect the Kafka producer:

async shutdown(): Promise<void> {
  await this.kafkaCient.disconnectProducer();
  return new Promise((resolve) => {
    this.server.tryShutdown(() => {
      this.logger.info("gRPC server shut down gracefully");
      resolve();
    });
  });
}

Also fix the property name typo on line 11: rename kafkaCient to kafkaClient for consistency.

🧹 Nitpick comments (22)
packages/kafka-client/Readme.md (1)

1-6: Fix Markdown checkbox syntax.

The checkbox syntax is incorrect. In Markdown, checkboxes should have a space between the brackets: [ ] for unchecked and [x] for checked items.

Apply this diff to fix the checkbox syntax:

 Tasks:-
-[] Schema Registry
-[] Retry mechanism
-[] DLQ forwarding
-[] Graceful shutdown
-[] Compression
+- [ ] Schema Registry
+- [ ] Retry mechanism
+- [ ] DLQ forwarding
+- [ ] Graceful shutdown
+- [ ] Compression
turbo.json (1)

35-37: Optional: Add explicit empty outputs for consistency.

While not strictly required, adding "outputs": [] would align with the pattern used by similar tasks like infra:up (Line 33).

 "db:studio": {
-  "cache": false
+  "cache": false,
+  "outputs": []
 },
apps/order-service/README.md (1)

32-36: Optional: Improve readability by varying language.

The repeated use of "Only" in consecutive bullet points makes the text slightly repetitive.

 - **`order-service`** (or `order-grpc-service`):
-  - Only handles gRPC requests
-  - Only produces to Kafka
+  - Handles gRPC requests
+  - Produces events to Kafka
   - Can be scaled based on gRPC request load
apps/order-worker-service/src/config/dotenv.ts (1)

9-15: Consider extracting shared configuration pattern.

This environment configuration pattern is duplicated across multiple services (api-gateway, order-service, order-worker-service). Consider extracting this into a shared utility function in a common package to reduce duplication and ensure consistency.

infra/docker/db/docker-compose.yml (1)

31-31: Remove or document commented init-scripts mount.

The commented-out init-scripts volume mount suggests incomplete setup or future work. Either uncomment it if initialization scripts are needed, remove it if not planned, or add a comment explaining why it's kept.

packages/kafka-client/src/constants.ts (1)

1-26: Consider using const assertions for better type safety.

The constant objects could benefit from TypeScript as const assertions to ensure immutability and enable stricter type narrowing. This prevents accidental modifications and provides better IntelliSense.

Apply this pattern to improve type safety:

-const KAFKA_TOPICS = {
+const KAFKA_TOPICS = {
   ORDERS: "orders",
   TRADES: "trades",
   PAYMENTS: "payments",
   USERS: "users",
-};
+} as const;

-const ORDER_EVENTS = {
+const ORDER_EVENTS = {
   CREATE: "create",
   UPDATE: "update",
   DELETE: "delete",
-};
+} as const;

-const KAFKA_CONSUMER_GROUP_ID = {
+const KAFKA_CONSUMER_GROUP_ID = {
   ORDER_CONSUMER_SERVICE: "order-consumer-service",
   ORDER_CONSUMER_SERVICE_1: "order-consumer-service-1",
   ORDER_CONSUMER_SERVICE_2: "order-consumer-service-2",
   ORDER_CONSUMER_SERVICE_3: "order-consumer-service-3",
-};
+} as const;

-const KAFKA_CLIENT_ID = {
+const KAFKA_CLIENT_ID = {
   ORDER_PRODUCER_SERVICE: "order-producer-service",
   ORDER_CONSUMER_SERVICE: "order-consumer-service",
-};
+} as const;
package.json (1)

25-39: Consider extracting docker-compose command pattern.

The docker-compose commands are highly repetitive with only the service path changing. While functional, consider whether a helper script could reduce duplication and make maintenance easier.

Example approach: Create a shell script like scripts/infra-compose.sh:

#!/bin/bash
# Usage: ./scripts/infra-compose.sh <service> <command>
SERVICE=$1
COMMAND=$2
docker compose --env-file .env -f "infra/docker/${SERVICE}/docker-compose.yml" ${COMMAND}

Then update package.json:

"infra:db:up": "./scripts/infra-compose.sh db 'up -d'",
"infra:db:down": "./scripts/infra-compose.sh db down",
apps/order-worker-service/.env.example (1)

1-3: Address linter warnings for consistency.

The static analysis tool flagged two style issues:

  1. Variables should be alphabetically ordered (KAFKA_BROKERS before NODE_ENV)
  2. Missing blank line at end of file

While minor, addressing these improves consistency across environment files.

Apply this diff:

-NODE_ENV=
 KAFKA_BROKERS=
+NODE_ENV=
 ORDER_SERVICE_GRPC_URL=
+
apps/order-service/.env.example (1)

1-3: Address linter warnings for consistency.

Same as the order-worker-service, this file has style issues flagged by the linter:

  1. Variables should be alphabetically ordered (KAFKA_BROKERS before NODE_ENV)
  2. Missing blank line at end of file

Apply this diff:

-NODE_ENV=
 KAFKA_BROKERS=
+NODE_ENV=
 ORDER_SERVICE_GRPC_URL=
+
apps/api-gateway/src/types/index.ts (1)

37-47: Consider removing commented code.

The commented-out type definitions (OrderSideKeys, OrderTypeKeys, Order) should be removed once the migration to PlaceOrder is confirmed stable. Commented code can clutter the codebase and create confusion.

apps/order-worker-service/src/index.ts (1)

8-18: Consider refactoring duplicated shutdown logic.

Both SIGINT and SIGTERM handlers contain identical shutdown logic. Consider extracting this into a shared function to reduce duplication.

Apply this diff to refactor:

+const gracefulShutdown = async (signal: string) => {
+  logger.info(`Received ${signal}, shutting down gracefully`);
+  await kafkaConsumerInstance.shutdown();
+  process.exit(0);
+};
+
 // Graceful shutdown
-process.on("SIGINT", async () => {
-  logger.info("Received SIGINT, shutting down gracefully");
-  await kafkaConsumerInstance.shutdown();
-  process.exit(0);
-});
+process.on("SIGINT", () => gracefulShutdown("SIGINT"));

-process.on("SIGTERM", async () => {
-  logger.info("Received SIGTERM, shutting down gracefully");
-  await kafkaConsumerInstance.shutdown();
-  process.exit(0);
-});
+process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
.env.example (1)

1-11: LGTM with minor style suggestions.

The environment template provides sensible defaults for local development. The static analysis warnings about key ordering and the missing blank line at the end are minor style issues that can be addressed optionally.

If you want to address the linter warnings, apply this diff:

 # ClickHouse
 CLICKHOUSE_DB=nerve
-CLICKHOUSE_USER=nerve
 CLICKHOUSE_PASSWORD=nerve
+CLICKHOUSE_USER=nerve

 # PostgreSQL
 POSTGRES_DB=nerve
-POSTGRES_USER=nerve
 POSTGRES_PASSWORD=nerve
+POSTGRES_USER=nerve
+
packages/validator/src/index.ts (1)

2-2: Consider removing the blanket zod re-export.

Re-exporting the entire Zod library increases the package's API surface and could lead to version conflicts if consumers already depend on Zod directly.

If Zod types are needed by consumers, consider exporting only the specific types required (e.g., z, ZodSchema) or let consumers import Zod directly.

-export * from "zod";
+export { z, type ZodSchema } from "zod"; // Export only what's needed, or remove entirely
apps/order-worker-service/README.md (1)

32-35: Minor: Consider varying the adverb usage.

The word "Only" appears twice in consecutive bullet points, which slightly affects readability.

Consider rewording for variety:

 - **`order-service`** (or `order-grpc-service`):
-  - Only handles gRPC requests
-  - Only produces to Kafka
+  - Handles gRPC requests exclusively
+  - Produces to Kafka only
   - Can be scaled based on gRPC request load
packages/validator/src/Order.ts (2)

6-6: Add validation constraints for symbol field.

The symbol field accepts any string without format or length validation. Consider adding constraints to ensure valid trading pair symbols.

Apply this diff to add basic symbol validation:

-      symbol: z.string(),
+      symbol: z.string().min(1).max(20).regex(/^[A-Z0-9]+$/),

7-8: Consider adding upper bounds for price and quantity.

While positive integer validation prevents negative values, there are no upper bounds on price and quantity. This could allow unrealistic values that might cause issues downstream.

Consider adding reasonable upper bounds:

-      price: z.number().int().positive().optional(),
-      quantity: z.number().int().positive(),
+      price: z.number().int().positive().max(1000000000).optional(),
+      quantity: z.number().int().positive().max(1000000000),
apps/order-worker-service/src/controllers/order.controller.ts (1)

35-35: Hardcoded time_in_force value may not suit all order types.

The time_in_force field is hardcoded to "GTC" (Good Till Cancel), which may not be appropriate for all order types. For example, some traders may want "IOC" (Immediate or Cancel) or "FOK" (Fill or Kill) for certain strategies.

Consider making time_in_force a parameter in the order request or deriving it based on order type:

         remaining_quantity: data.quantity,
-        time_in_force: "GTC",
+        time_in_force: data.timeInForce ?? "GTC",
         filled_quantity: 0,

You'll also need to add timeInForce to the CreateOrderRequest proto definition and validator.

apps/order-worker-service/src/kafka.consumer.ts (1)

38-47: Add error handling for controller operations.

The call to this.orderController.createOrder(data) on Line 42 is not wrapped in a try-catch block. While the Kafka client's subscribe method (in packages/kafka-client/src/index.ts lines 90-100) catches errors at the message processing level, explicit error handling here would provide better context and allow for event-specific error recovery strategies.

Consider adding explicit error handling:

           case "create":
             this.logger.info(`Processing order creation for order ID:`, data.id);

-            await this.orderController.createOrder(data);
+            try {
+              await this.orderController.createOrder(data);
+            } catch (error) {
+              this.logger.error("Order creation failed", {
+                orderId: data.id,
+                error: error instanceof Error ? error.message : String(error),
+              });
+              throw error; // Re-throw to let Kafka client handle retry/DLQ
+            }

             break;
packages/kafka-client/src/index.ts (1)

67-67: Consider making fromBeginning configurable.

The fromBeginning: false setting (Line 67) means new consumers will only process new messages. While this may be intentional, consider making it a parameter to allow flexibility for different use cases (e.g., reprocessing historical events).

   async subscribe<T>(
     groupId: string,
     topic: string,
     handler: (message: T, topic: string, partition: number) => Promise<void>,
     schema?: z.ZodTypeAny,
+    fromBeginning: boolean = false,
   ) {
     const consumer = await this.createConsumer(groupId);
-    await consumer.subscribe({ topic, fromBeginning: false });
+    await consumer.subscribe({ topic, fromBeginning });
apps/order-worker-service/src/constants.ts (1)

1-25: Consider adding reverse mappings for bidirectional conversion.

The current implementation only provides number-to-string mappings. If you ever need to convert from string back to enum (e.g., when reading from external APIs or config files), consider adding reverse mappings.

// String to number mappings
export const OrderStatusStringEnumMap: Record<string, Status> = Object.fromEntries(
  Object.entries(OrderStatusEnumStringMap).map(([k, v]) => [v, Number(k)])
) as Record<string, Status>;
apps/api-gateway/src/middlewares/zod.validator.middleware.ts (1)

19-28: Type assertions reduce type safety but are necessary for Express.

The as any casts on lines 26-27 bypass TypeScript's type checking for req.query and req.params. While this is a common pattern when integrating validation middleware with Express, be aware that it can mask type mismatches at compile time.

Consider documenting that schemas must output types compatible with Express's expectations for query (string | string[]) and params (string).

apps/order-service/src/controllers/order.controller.ts (1)

28-29: Track the matching engine integration as a follow-up task.

The comment indicates that placing orders to the matching engine is not yet implemented. This is critical functionality for order processing.

Would you like me to open a new issue to track the matching engine integration? This would ensure the work isn't forgotten and can be prioritized appropriately.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3361519 and 8fa0332.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (54)
  • .env.example (1 hunks)
  • apps/api-gateway/.env.example (1 hunks)
  • apps/api-gateway/package.json (2 hunks)
  • apps/api-gateway/src/config/dotenv.ts (1 hunks)
  • apps/api-gateway/src/controllers/order.controller.ts (1 hunks)
  • apps/api-gateway/src/controllers/user.controller.ts (1 hunks)
  • apps/api-gateway/src/middlewares/zod.validator.middleware.ts (1 hunks)
  • apps/api-gateway/src/routers/order.route.ts (1 hunks)
  • apps/api-gateway/src/types/index.ts (2 hunks)
  • apps/order-service/.env.example (1 hunks)
  • apps/order-service/README.md (1 hunks)
  • apps/order-service/package.json (1 hunks)
  • apps/order-service/src/config/dotenv.ts (1 hunks)
  • apps/order-service/src/controllers/order.controller.ts (1 hunks)
  • apps/order-service/src/grpc.server.ts (1 hunks)
  • apps/order-service/src/index.ts (1 hunks)
  • apps/order-service/src/services/order.service.ts (0 hunks)
  • apps/order-worker-service/.env.example (1 hunks)
  • apps/order-worker-service/README.md (1 hunks)
  • apps/order-worker-service/eslint.config.mjs (1 hunks)
  • apps/order-worker-service/nodemon.json (1 hunks)
  • apps/order-worker-service/package.json (1 hunks)
  • apps/order-worker-service/src/config/dotenv.ts (1 hunks)
  • apps/order-worker-service/src/constants.ts (1 hunks)
  • apps/order-worker-service/src/controllers/order.controller.ts (1 hunks)
  • apps/order-worker-service/src/index.ts (1 hunks)
  • apps/order-worker-service/src/kafka.consumer.ts (1 hunks)
  • apps/order-worker-service/tsconfig.json (1 hunks)
  • infra/docker/.env.example (0 hunks)
  • infra/docker/clickhouse/docker-compose.yml (1 hunks)
  • infra/docker/db/docker-compose.yml (1 hunks)
  • infra/docker/docker-compose.yml (0 hunks)
  • infra/docker/kafka/create-topics.sh (1 hunks)
  • infra/docker/kafka/docker-compose.yml (1 hunks)
  • package.json (1 hunks)
  • packages/kafka-client/Readme.md (1 hunks)
  • packages/kafka-client/eslint.config.mjs (1 hunks)
  • packages/kafka-client/package.json (1 hunks)
  • packages/kafka-client/src/constants.ts (1 hunks)
  • packages/kafka-client/src/index.ts (1 hunks)
  • packages/kafka-client/tsconfig.json (1 hunks)
  • packages/prisma/package.json (1 hunks)
  • packages/prisma/src/index.ts (1 hunks)
  • packages/types/index.d.ts (1 hunks)
  • packages/types/order.d.ts (1 hunks)
  • packages/types/package.json (1 hunks)
  • packages/types/tsconfig.json (1 hunks)
  • packages/types/user.d.ts (1 hunks)
  • packages/validator/eslint.config.mjs (1 hunks)
  • packages/validator/package.json (1 hunks)
  • packages/validator/src/Order.ts (1 hunks)
  • packages/validator/src/index.ts (1 hunks)
  • packages/validator/tsconfig.json (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (3)
  • apps/order-service/src/services/order.service.ts
  • infra/docker/.env.example
  • infra/docker/docker-compose.yml
🧰 Additional context used
🧬 Code graph analysis (13)
packages/prisma/package.json (1)
packages/prisma/src/client.ts (1)
  • createPrismaClient (9-13)
packages/kafka-client/src/index.ts (1)
packages/logger/src/index.ts (2)
  • Logger (115-115)
  • logger (109-109)
apps/order-service/src/config/dotenv.ts (1)
apps/api-gateway/src/config/dotenv.ts (1)
  • env (9-18)
apps/order-worker-service/src/index.ts (1)
packages/logger/src/index.ts (1)
  • logger (109-109)
apps/api-gateway/src/routers/order.route.ts (3)
apps/api-gateway/src/config/dotenv.ts (1)
  • env (9-18)
packages/validator/src/Order.ts (1)
  • PlaceOrderValidator (3-25)
apps/api-gateway/src/types/index.ts (1)
  • CreateOrderRequest (49-51)
apps/order-worker-service/eslint.config.mjs (1)
lint-staged.config.js (1)
  • filenames (5-29)
apps/order-worker-service/src/controllers/order.controller.ts (3)
apps/order-service/src/controllers/order.controller.ts (1)
  • OrderServerController (7-56)
packages/logger/src/index.ts (2)
  • logger (109-109)
  • Logger (115-115)
packages/prisma/src/repositories/order.ts (1)
  • OrderRepository (5-99)
apps/order-service/src/controllers/order.controller.ts (3)
apps/order-worker-service/src/controllers/order.controller.ts (1)
  • OrderServerController (12-48)
packages/logger/src/index.ts (2)
  • logger (109-109)
  • Logger (115-115)
packages/kafka-client/src/index.ts (1)
  • KafkaClient (116-116)
apps/order-worker-service/src/kafka.consumer.ts (4)
apps/order-service/src/controllers/order.controller.ts (1)
  • OrderServerController (7-56)
apps/order-worker-service/src/controllers/order.controller.ts (1)
  • OrderServerController (12-48)
packages/prisma/src/repositories/order.ts (1)
  • OrderRepository (5-99)
apps/order-worker-service/src/config/dotenv.ts (1)
  • env (9-15)
apps/order-service/src/index.ts (3)
apps/order-service/src/grpc.server.ts (1)
  • GrpcServer (8-60)
packages/logger/src/index.ts (1)
  • logger (109-109)
apps/order-service/src/config/dotenv.ts (1)
  • env (9-15)
apps/api-gateway/src/types/index.ts (1)
packages/validator/src/Order.ts (1)
  • PlaceOrder (27-27)
packages/prisma/src/index.ts (2)
packages/prisma/src/repositories/trade.ts (2)
  • TradeRepository (4-74)
  • constructor (5-5)
packages/types/trade.d.ts (2)
  • TradeData (13-16)
  • Trade (3-11)
apps/order-service/src/grpc.server.ts (5)
packages/kafka-client/src/index.ts (1)
  • KafkaClient (116-116)
packages/logger/src/index.ts (2)
  • logger (109-109)
  • Logger (115-115)
packages/kafka-client/src/constants.ts (1)
  • KAFKA_CLIENT_ID (26-26)
apps/order-service/src/config/dotenv.ts (1)
  • env (9-15)
apps/order-service/src/controllers/order.controller.ts (1)
  • OrderServerController (7-56)
🪛 dotenv-linter (4.0.0)
.env.example

[warning] 6-6: [UnorderedKey] The CLICKHOUSE_PASSWORD key should go before the CLICKHOUSE_USER key

(UnorderedKey)


[warning] 11-11: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 11-11: [UnorderedKey] The POSTGRES_PASSWORD key should go before the POSTGRES_USER key

(UnorderedKey)

apps/order-service/.env.example

[warning] 2-2: [UnorderedKey] The KAFKA_BROKERS key should go before the NODE_ENV key

(UnorderedKey)


[warning] 3-3: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

apps/order-worker-service/.env.example

[warning] 2-2: [UnorderedKey] The KAFKA_BROKERS key should go before the NODE_ENV key

(UnorderedKey)


[warning] 3-3: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🪛 LanguageTool
apps/order-service/README.md

[style] ~34-~34: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...e`): - Only handles gRPC requests - Only produces to Kafka - Can be scaled bas...

(ADVERB_REPETITION_PREMIUM)

apps/order-worker-service/README.md

[style] ~34-~34: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...e`): - Only handles gRPC requests - Only produces to Kafka - Can be scaled bas...

(ADVERB_REPETITION_PREMIUM)

🔇 Additional comments (55)
packages/prisma/package.json (1)

18-18: LGTM!

The addition of the db:studio script is a helpful developer tool that launches Prisma Studio for database visualization and management.

packages/types/package.json (1)

19-19: LGTM!

Adding @repo/proto-defs as a devDependency is appropriate for this types package, as it's likely used during type generation and development workflows.

packages/validator/tsconfig.json (1)

1-10: LGTM!

The TypeScript configuration follows standard conventions and aligns with other packages in the monorepo.

packages/types/tsconfig.json (1)

8-9: LGTM!

The path mapping for proto-defs enables cleaner import resolution for generated TypeScript types.

apps/order-worker-service/nodemon.json (1)

1-6: LGTM!

The nodemon configuration is standard and appropriate for TypeScript development with path mapping support.

apps/api-gateway/src/controllers/user.controller.ts (1)

21-23: Verify the @repo/types User type definition and migration strategy.

The controller, service, and database layer are internally consistent with the username field. However, the User type definition is imported from the shared @repo/types package, which cannot be verified from this codebase. Additionally, there is no evidence of migration files, deprecation handling, or backward compatibility strategy.

Verify that:

  1. The User type in @repo/types defines username (not name)
  2. A data migration strategy exists for existing users
  3. API clients external to this repository have been updated
  4. API documentation has been updated to reflect the field name change
packages/validator/eslint.config.mjs (1)

1-3: LGTM!

Clean ESLint configuration that follows the established pattern of delegating to the shared node config.

packages/kafka-client/eslint.config.mjs (1)

1-3: LGTM!

Clean ESLint configuration that follows the established pattern of delegating to the shared node config.

apps/order-service/README.md (1)

1-47: Excellent architectural documentation!

This README clearly articulates the current issues (tight coupling, scaling limitations) and proposes a clean separation of concerns. The TODO list provides a clear roadmap for implementation.

apps/order-worker-service/tsconfig.json (1)

1-14: LGTM!

Well-configured TypeScript setup with appropriate path mappings for the order-worker-service. The proto-defs path alias correctly points to generated TypeScript definitions.

apps/order-worker-service/eslint.config.mjs (1)

1-3: LGTM!

Clean ESLint configuration that follows the established pattern of delegating to the shared node config.

infra/docker/clickhouse/docker-compose.yml (1)

10-27: Well-configured service settings.

The environment variables, volumes, healthcheck, and ulimits are appropriately configured for ClickHouse. Notable good practices:

  • Init scripts mounted read-only for security
  • Proper healthcheck endpoint
  • Appropriate file descriptor limits for production use
packages/types/user.d.ts (2)

7-9: Good addition of id property.

Adding id to UserData is a sensible extension that aligns with typical data models where the base type represents input/creation data and the extended type includes system-generated fields like IDs.


1-5: No breaking references remain—migration is complete.

Verification confirms all references to the User.name property have been properly updated to User.username throughout the codebase. No dangling references were found, and all related types (CreateUserRequest, UpdateUserRequest, UserData) correctly reference the updated User definition from @repo/types.

packages/prisma/src/index.ts (1)

6-6: Verify the intent of wildcard type export.

Changed from explicitly exporting User, Order, Trade, and Prisma types to exporting all types with export type *. This will expose all generated Prisma types, including potentially internal ones. Please confirm this is intentional and that you want to expose the entire Prisma type surface publicly.

package.json (1)

20-24: Sequential execution may fail silently on errors.

The composite infrastructure scripts use && to chain commands sequentially (e.g., infra:db:up && infra:kafka:up && infra:clickhouse:up). If any intermediate command fails, subsequent commands won't execute, but the overall error handling depends on shell behavior. Consider documenting this or adding explicit error handling if needed.

apps/order-worker-service/src/config/dotenv.ts (1)

5-7: Confirm NODE_ENV is set in your deployment and CI environment.

The pattern of accessing process.env.NODE_ENV before dotenv loads is consistent across your services (api-gateway, order-service, order-worker-service, and express-base-template). While there's a safe "development" fallback, this creates an implicit dependency on NODE_ENV being set by your shell/process environment or deployment configuration rather than from the .env file.

Ensure NODE_ENV is explicitly set in your:

  • CI/CD pipeline environment variables
  • Docker/container environment setup
  • Local development shell environment

This is typically intentional—NODE_ENV is standard practice to configure at the deployment level—but verify it's documented and configured consistently across all your environments.

infra/docker/db/docker-compose.yml (1)

7-8: <!-- [scratchpad_start]
[task verify_redis_port_config COMPLETE]
[observations]

  • docker-compose.yml: Redis service with 6380:6379 port mapping (confirmed)
  • NO redis-lib directory exists (mentioned in docs but not implemented)
  • NO npm redis packages installed
  • NO Redis client code anywhere in codebase
  • NO services configured to connect to Redis (port 6380 OR any other port)
  • Port mapping 6380:6379 is correct Docker syntax: host:container
  • Documentation indicates Redis is planned but implementation incomplete

[analysis]
CRITICAL: The review comment assumes "services are configured to connect to port 6380"
This assumption is INCORRECT:

  1. Zero services currently connect to Redis
  2. No Redis client initialization exists
  3. No environment variables or connection strings for Redis
  4. The docker-compose setup is infrastructure-only
  5. Internal Docker services would use redis:6379 anyway, not 6380

The review comment's concern is invalid because:

  • It addresses a non-existent problem (services not yet implemented)
  • The port mapping itself is benign and correctly configured
  • The host port (6380) is irrelevant to internal Docker networking

[conclusion]
The review comment is INCORRECT/PREMATURE. The developer should ignore this comment as there are no services to misconfigure, and the docker-compose setup itself is sound.

[tag]

</scratchpad_end] -->

No verification needed—the review comment is incorrect.

The concern about services being "configured to connect to port 6380" cannot be validated because no services currently connect to Redis. A codebase-wide search reveals zero Redis client implementations, no Redis npm dependencies, and no connection strings. The docker-compose port mapping 6380:6379 (host:container) is correctly configured and will not cause issues; services within the Docker network would connect via redis:6379 internally, regardless of the host port. The redis-lib mentioned in documentation does not yet exist. This is infrastructure setup awaiting application implementation.

Likely an incorrect or invalid review comment.

packages/types/index.d.ts (1)

3-3: LGTM!

The export addition correctly exposes order-related types to consumers of the types package, supporting the broader Kafka-based order processing implementation.

apps/api-gateway/.env.example (1)

11-12: LGTM!

The ORDER_SERVICE_GRPC_URL environment variable addition properly externalizes the gRPC service URL configuration, supporting flexible deployment environments.

apps/api-gateway/src/types/index.ts (2)

3-3: Good migration to shared types.

The import of PlaceOrder from the validator package promotes type consistency across services.


50-50: LGTM!

The CreateOrderRequest interface now correctly uses PlaceOrder["body"] from the validator package, ensuring consistent validation and typing across the order creation flow.

apps/order-worker-service/src/index.ts (1)

1-5: LGTM!

The Kafka consumer initialization and startup logic is clear and straightforward.

packages/kafka-client/tsconfig.json (1)

1-10: LGTM!

The TypeScript configuration is standard and properly set up for the Kafka client package, with appropriate source/output directories and exclusions.

apps/api-gateway/src/config/dotenv.ts (1)

16-17: LGTM!

The ORDER_SERVICE_GRPC_URL environment variable is properly validated and aligns with the usage in the order route.

packages/types/order.d.ts (1)

1-4: LGTM!

The type aliases correctly derive keys from the proto-generated enums using the keyof typeof pattern.

apps/api-gateway/src/routers/order.route.ts (3)

8-10: LGTM!

The imports support the new validation middleware and environment-based configuration.


14-25: LGTM!

The gRPC client configuration now uses the environment variable instead of a hardcoded URL, improving configurability across environments.


29-34: LGTM!

Request validation is properly applied via middleware before the controller handler, ensuring type-safe and validated payloads.

infra/docker/kafka/create-topics.sh (1)

1-48: LGTM! Comprehensive topic setup script.

The script properly waits for brokers, creates topics with production-ready configurations (replication factor 3, min.insync.replicas=2), and provides detailed output for verification. The use of --if-not-exists makes it idempotent. The infrastructure is correctly configured with 3 brokers to satisfy these requirements.

packages/validator/package.json (1)

23-23: No issues found — the Zod version is correct.

The latest stable Zod release is 4.1.12, which matches the version specified in the package.json. The original review comment is incorrect and can be dismissed.

Likely an incorrect or invalid review comment.

apps/order-service/src/config/dotenv.ts (2)

1-7: LGTM: Environment loading setup looks correct.

The dotenv configuration properly loads environment files based on NODE_ENV with a sensible fallback to "development". Path resolution using __dirname should work correctly with the typical TypeScript compilation output structure.


9-15: LGTM: Environment validation is appropriate.

The environment variable validation covers all necessary configuration for the order service: gRPC URL, environment mode, and Kafka brokers. The NODE_ENV choices align with standard environments.

packages/validator/src/Order.ts (1)

13-25: LGTM: Refinement logic correctly enforces price requirement.

The refinement properly validates that price is required for LIMIT, STOP, and STOP_LIMIT orders while keeping it optional for MARKET orders. The error message and path are clear and actionable.

apps/order-service/src/grpc.server.ts (2)

23-34: LGTM: Service initialization is properly structured.

The initialize() method correctly instantiates the controller with dependencies and registers the service implementation with the gRPC server. The binding ensures proper context for the controller method.


36-50: LGTM: Async server startup with proper error handling.

The start() method properly wraps the async bindAsync operation in a Promise, handles errors, and logs the server startup. The use of insecure credentials is acceptable for development environments.

Ensure that production deployments use secure credentials instead of grpc.ServerCredentials.createInsecure().

infra/docker/kafka/docker-compose.yml (4)

7-39: LGTM: Kafka broker 1 is properly configured.

The first broker configuration includes all necessary KRaft settings, listeners, quorum voters, replication factors, and health checks. The environment variables are comprehensive and appropriate for a production-like setup.


30-30: Verify log cleanup policy choice.

The KAFKA_LOG_CLEANUP_POLICY: compact setting is configured for all brokers. Log compaction is suitable for topics where you only need the latest value per key (e.g., state topics), but it may not be appropriate for event streaming topics like "orders" where you need full event history.

Verify that log compaction aligns with your use case for order events. If you need full event history, consider using delete policy or making this topic-specific rather than a broker-level default.


108-122: LGTM: Kafka initialization service properly configured.

The kafka-init service correctly depends on all brokers being healthy before executing the topic creation script. The volume mount and entrypoint are properly set up.


127-143: LGTM: Kafka UI service configuration is correct.

The Kafka UI service is properly configured with dependencies on all brokers and the correct bootstrap servers. This will be useful for monitoring and debugging.

apps/order-worker-service/src/controllers/order.controller.ts (2)

40-46: LGTM: Error handling properly logs and rethrows.

The error handling correctly logs the error message and rethrows to allow upstream handling. This ensures errors are both logged and can be handled by Kafka consumer retry logic.


27-29: Enum mappings are correct and align with Prisma schema.

Verification confirms that all values in OrderSideEnumStringMap, OrderTypeEnumStringMap, and OrderStatusEnumStringMap match their corresponding Prisma schema enum definitions exactly. No misalignments or runtime errors will occur.

apps/order-worker-service/src/kafka.consumer.ts (3)

20-27: LGTM: Consumer initialization is properly structured.

The constructor correctly initializes the Kafka client, repository, and controller with appropriate dependencies. The broker configuration is properly parsed from the environment variable.


29-50: LGTM: Message consumption logic is well-structured.

The startConsuming() method properly subscribes to the Kafka topic, logs incoming messages, and uses a switch statement for extensibility to handle additional event types in the future. The warning for unknown event types is a good defensive practice.


52-55: LGTM: Shutdown properly disconnects the consumer.

The shutdown() method correctly calls the Kafka client's disconnect method and logs the shutdown event.

packages/kafka-client/src/index.ts (4)

11-14: LGTM: Constructor properly initializes Kafka client with logging.

The constructor correctly instantiates the Kafka client with the provided clientId and brokers, and sets up a child logger with the component context for better log traceability.


16-23: LGTM: Lazy producer initialization with idempotency enabled.

The getProducer() method implements lazy initialization correctly, creating the producer only once and enabling idempotency for reliable message delivery. This is a best practice for Kafka producers.


32-58: LGTM: Message sending with optional validation is well-implemented.

The sendMessage() method properly validates the topic, optionally validates the payload against a schema, and sends the message with appropriate logging. The safe parse approach prevents throwing on validation errors until explicitly needed.


60-88: LGTM: Subscribe method implements proper manual offset management.

The subscribe() method correctly:

  • Creates a consumer with the specified group ID
  • Subscribes to the topic
  • Implements manual offset commits for precise control
  • Parses and optionally validates messages
  • Provides clear logging

The manual commit strategy (Line 83-89) ensures messages are only marked as processed after successful handling.

apps/order-worker-service/src/constants.ts (2)

3-11: LGTM: Order status mapping is comprehensive.

The OrderStatusEnumStringMap correctly maps all order status enum values from the proto definitions to their string representations, covering the full order lifecycle from PENDING through EXPIRED.


20-23: LGTM: Order side mapping is correct.

The OrderSideEnumStringMap correctly maps BUY and SELL enum values, which are the standard order sides for trading.

apps/api-gateway/src/middlewares/zod.validator.middleware.ts (1)

6-17: LGTM! Clean validation pattern.

The middleware correctly validates request data using Zod's safeParse and properly formats validation errors with treeifyError before passing them to Express error handling.

apps/order-service/src/controllers/order.controller.ts (3)

8-11: LGTM! Clean dependency injection.

The constructor correctly injects KafkaClient, aligning with the new event-driven architecture.


30-38: LGTM! Response structure is well-formed.

The response correctly includes the generated orderId, sets the status to PENDING, and provides clear success feedback to the client.


39-54: LGTM! Robust error handling.

The error handling properly logs both the error message and stack trace, and correctly returns a gRPC ServiceError with appropriate status codes.

"@repo/eslint-config": "workspace:*",
"@repo/types": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@repo/validator": "workspace:*",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify dependency placement for @repo/validator.

The summary indicates that @repo/validator is used in runtime middleware (zod.validator.middleware.ts) for request validation. If the validator package is imported and used at runtime (not just during development or build time), it should be in dependencies rather than devDependencies.

Run the following script to check if the validator is imported in production code:


🏁 Script executed:

#!/bin/bash
# Description: Check if @repo/validator is imported in runtime code (non-test, non-dev files)

# Search for imports of @repo/validator in the api-gateway source code
rg -n --type=ts -g '!**/*.test.ts' -g '!**/*.spec.ts' '@repo/validator' apps/api-gateway/src/

Length of output: 289


🏁 Script executed:

#!/bin/bash
# Check the dependencies section layout in package.json
cat apps/api-gateway/package.json | jq '.dependencies | has("@repo/validator"), .devDependencies | has("@repo/validator")'

Length of output: 61


🏁 Script executed:

#!/bin/bash
# Extract the full package.json to check dependency placement
cat apps/api-gateway/package.json | jq '{dependencies: .dependencies | keys | map(select(. == "@repo/validator")), devDependencies: .devDependencies | keys | map(select(. == "@repo/validator"))}'

Length of output: 74


Move @repo/validator from devDependencies to dependencies.

Verification confirms @repo/validator is imported and used at runtime in three files:

  • zod.validator.middleware.ts (imports z for validation middleware)
  • order.route.ts (imports PlaceOrderValidator for route handling)
  • types/index.ts (type imports)

Runtime dependencies cannot be placed in devDependencies as they will not be available in production builds. Move @repo/validator to the dependencies section of apps/api-gateway/package.json.

🤖 Prompt for AI Agents
In apps/api-gateway/package.json around line 35, @repo/validator is currently
listed under devDependencies but is imported and used at runtime; remove the
"@repo/validator": "workspace:*" entry from devDependencies and add the exact
same entry under the top-level dependencies object so the package is installed
in production builds, preserving the workspace:* version string and updating
package.json ordering/commas as needed.

const grpcRequest: GrpcCreateOrderRequest = {
symbol,
price,
price: price!, // TODO: price is optional in proto, but required for limit orders
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Validate price upstream instead of using non-null assertion.

The non-null assertion operator (price!) is unsafe and can lead to runtime errors if the price is actually null or undefined. The TODO comment acknowledges that price is optional in the proto but required for limit orders.

Ensure that:

  1. The PlaceOrder validator enforces price as required for limit orders before reaching this controller
  2. Consider adding explicit validation here or handling the optional price case properly

Would you like me to help verify the validator implementation or propose a safer approach?


🏁 Script executed:

#!/bin/bash
# View the order controller file around lines 24 and 28
cat apps/api-gateway/src/controllers/order.controller.ts | head -40 | tail -20

Length of output: 668


🏁 Script executed:

#!/bin/bash
# Search for PlaceOrder validator or validation logic
rg -n "PlaceOrder|validator" apps/api-gateway/src --type=ts -A 3 -B 3

Length of output: 2636


🏁 Script executed:

#!/bin/bash
# Search for userId assignments and hardcoded values
rg -n "userId.*=.*['\"]" apps/api-gateway/src/controllers/order.controller.ts -A 2 -B 2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for price in proto definitions
fd -e proto | head -20

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Check authentication context usage in controller
rg -n "auth|user|context" apps/api-gateway/src/controllers/order.controller.ts -i -C 2

Length of output: 200


🏁 Script executed:

#!/bin/bash
# Find PlaceOrderValidator definition
fd -e ts -e js | xargs rg -l "PlaceOrderValidator|export.*PlaceOrder" | head -10

Length of output: 75


🏁 Script executed:

#!/bin/bash
# Look for auth middleware in the application
rg -n "middleware|auth|jw" apps/api-gateway/src/middlewares --type=ts -i | head -30

Length of output: 237


🏁 Script executed:

#!/bin/bash
# Check order service proto for price field definition
cat packages/proto-defs/proto/order_service.proto

Length of output: 1072


🏁 Script executed:

#!/bin/bash
# Check the full order controller to see if userId is obtained from req anywhere
cat apps/api-gateway/src/controllers/order.controller.ts

Length of output: 1312


🏁 Script executed:

#!/bin/bash
# Search for environment variables or config that might define the hardcoded userId
rg -n "d8036c81-a1d7-45de-b4d8-e3847bfadd3b" apps/

Length of output: 109


🏁 Script executed:

#!/bin/bash
# View PlaceOrderValidator schema definition
cat packages/validator/src/Order.ts

Length of output: 698


🏁 Script executed:

#!/bin/bash
# Check if there's any auth context middleware or user extraction in route setup
rg -n "app\.|router\.|use|auth" apps/api-gateway/src/routers/order.route.ts -C 3

Length of output: 221


🏁 Script executed:

#!/bin/bash
# Search for how the app is set up - main index/server file
fd -name "index.ts" -o -name "main.ts" -o -name "server.ts" | grep "api-gateway"

Length of output: 230


🏁 Script executed:

#!/bin/bash
# Find main app setup file
find apps/api-gateway/src -name "*.ts" -type f | grep -E "(index|app|main|server)" | head -5

Length of output: 223


🏁 Script executed:

#!/bin/bash
# Check if user info is attached to request anywhere
rg -n "req\.user|req\.body\.user|userId" apps/api-gateway/src --type=ts -B 2 -A 2

Length of output: 3413


🏁 Script executed:

#!/bin/bash
# Look for any authentication or JWT middleware in the entire app
find apps/api-gateway/src -name "*.ts" -type f -exec grep -l "auth\|jwt\|Bearer" {} \;

Length of output: 0


Fix hardcoded userId—all orders are attributed to the same test user.

The userId at line 28 is hardcoded to a test UUID, causing a critical issue where every order is attributed to one account. Extract userId from req.body instead, matching the pattern in trade.controller.ts (which reads userId from the request).

Regarding the price! assertion at line 24: The PlaceOrderValidator already guarantees price is defined for non-MARKET orders via its refine check, making the assertion redundant. Consider removing it or adding an inline guard clause for clarity.

🤖 Prompt for AI Agents
In apps/api-gateway/src/controllers/order.controller.ts around lines 24–28,
remove the hardcoded test UUID used for userId and instead read userId from
req.body (match the pattern used in trade.controller.ts), and replace the
hardcoded value with that extracted userId; also remove the unsafe non-null
assertion price! on line 24 (or add a short inline guard that throws/bails for
MARKET vs non-MARKET) because PlaceOrderValidator already ensures price is
defined for non‑MARKET orders—use the validated req.body value directly or a
simple runtime check if you prefer explicitness.

side: Side[side as keyof typeof Side],
type: Type[type as keyof typeof Type],
userId: "user-123",
userId: "d8036c81-a1d7-45de-b4d8-e3847bfadd3b",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded userId with authenticated user context.

The userId is hardcoded to a specific UUID ("d8036c81-a1d7-45de-b4d8-e3847bfadd3b"), which prevents proper user attribution in production. The userId should be extracted from the authenticated user's session or JWT token.

Consider extracting userId from the request authentication context:

-      userId: "d8036c81-a1d7-45de-b4d8-e3847bfadd3b",
+      userId: req.user?.id || req.headers['x-user-id'], // Adjust based on your auth strategy

Ensure proper authentication middleware is in place to populate the user context before this controller is invoked.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
userId: "d8036c81-a1d7-45de-b4d8-e3847bfadd3b",
userId: req.user?.id || req.headers['x-user-id'], // Adjust based on your auth strategy
🤖 Prompt for AI Agents
In apps/api-gateway/src/controllers/order.controller.ts around line 28, replace
the hardcoded userId value with the authenticated user's id pulled from the
request auth context (e.g., req.user.id or the decoded JWT subject) and ensure
the controller trusts an authentication middleware that populates req.user;
update the code to read userId from that field, add a guard that throws/returns
a 401 if no authenticated user is present, and remove the hardcoded UUID so
requests are attributed to the current authenticated user.

"@repo/logger": "workspace:*",
"@repo/proto-defs": "workspace:*",
"@repo/prisma": "workspace:*",
"@repo/kakfa-client": "workspace:*",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the package name typo.

The package name contains a typo: "@repo/kakfa-client" should be "@repo/kafka-client" (missing 'f' in kafka). This will cause package resolution to fail at runtime.

Apply this diff to fix the typo:

-    "@repo/kakfa-client": "workspace:*",
+    "@repo/kafka-client": "workspace:*",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@repo/kakfa-client": "workspace:*",
"@repo/kafka-client": "workspace:*",
🤖 Prompt for AI Agents
In apps/order-service/package.json around line 34, the dependency name is
misspelled as "@repo/kakfa-client"; update it to the correct package name
"@repo/kafka-client" so npm/yarn workspace resolution works; ensure the quotes
and trailing comma match surrounding entries and run install to verify
resolution.

Comment on lines +21 to +26
const orderId = crypto.randomUUID();

// Send this to kafka for order persistence
this.kafkaClient.sendMessage<
CreateOrderRequest & { id: string; status: Status; eventType: string }
>("orders", { id: orderId, status: Status.PENDING, eventType: "create", ...order });
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify that Kafka message is awaited before responding.

The sendMessage call doesn't appear to be awaited. If this is an asynchronous operation, the response might be sent to the client before the message is successfully published to Kafka, which could lead to:

  1. The client receiving success but the order never being persisted
  2. Unhandled promise rejections if the Kafka send fails

Please verify whether kafkaClient.sendMessage returns a Promise and if so, add await:

-      this.kafkaClient.sendMessage<
-        CreateOrderRequest & { id: string; status: Status; eventType: string }
-      >("orders", { id: orderId, status: Status.PENDING, eventType: "create", ...order });
+      await this.kafkaClient.sendMessage<
+        CreateOrderRequest & { id: string; status: Status; eventType: string }
+      >("orders", { id: orderId, status: Status.PENDING, eventType: "create", ...order });

🏁 Script executed:

#!/bin/bash
# Check the sendMessage signature in the KafkaClient implementation
ast-grep --pattern $'class KafkaClient {
  $$$
  sendMessage($$$) {
    $$$
  }
  $$$
}'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find KafkaClient class or interface definition
rg -n "class KafkaClient|interface KafkaClient" --type=ts

Length of output: 56


🏁 Script executed:

#!/bin/bash
# Look for sendMessage method definition
rg -n "sendMessage\s*\(" --type=ts -A 3 -B 1

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check imports in the order controller to understand kafkaClient source
cat apps/order-service/src/controllers/order.controller.ts | head -30

Length of output: 1073


🏁 Script executed:

#!/bin/bash
# Search for any async/await patterns with sendMessage
rg -n "await.*sendMessage|sendMessage.*await" --type=ts

Length of output: 0


🏁 Script executed:

#!/bin/bash
# View the KafkaClient implementation to see sendMessage signature
cat packages/kafka-client/src/index.ts

Length of output: 3289


🏁 Script executed:

#!/bin/bash
# Also check if there are any other methods or usage patterns in the KafkaClient file
rg -n "sendMessage" packages/kafka-client/src/index.ts -A 5

Length of output: 207


Add await to the sendMessage call.

The sendMessage method is async and returns a Promise (line 32 in packages/kafka-client/src/index.ts). Without await, the response is sent to the client before the message is published to Kafka, risking lost orders if the Kafka send fails.

-      this.kafkaClient.sendMessage<
-        CreateOrderRequest & { id: string; status: Status; eventType: string }
-      >("orders", { id: orderId, status: Status.PENDING, eventType: "create", ...order });
+      await this.kafkaClient.sendMessage<
+        CreateOrderRequest & { id: string; status: Status; eventType: string }
+      >("orders", { id: orderId, status: Status.PENDING, eventType: "create", ...order });
🤖 Prompt for AI Agents
In apps/order-service/src/controllers/order.controller.ts around lines 21 to 26,
the call to this.kafkaClient.sendMessage(...) is not awaited which allows the
HTTP response to be sent before the Kafka publish completes; add await before
this.kafkaClient.sendMessage(...) and ensure the enclosing handler is async (or
remains async) so execution waits for the publish to finish, and wrap the await
in a try/catch to handle/log publish errors and return an appropriate error
response if the send fails.

Comment on lines +76 to +103
kafka-3:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka-3
hostname: kafka-3
ports:
- "19094:19094"
environment:
KAFKA_NODE_ID: 3
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,EXTERNAL://0.0.0.0:19094,CONTROLLER://0.0.0.0:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-3:9092,EXTERNAL://localhost:19094
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka-1:9093,2@kafka-2:9093,3@kafka-3:9093
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"
volumes:
- kafka-3-data:/var/lib/kafka/data
networks:
- nerve-exchange-network
healthcheck:
test: ["CMD-SHELL", "kafka-broker-api-versions --bootstrap-server localhost:9092"]
interval: 10s
timeout: 5s
retries: 5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing environment variables on kafka-3.

Kafka-3 is missing the same configuration parameters as kafka-2:

  • KAFKA_NUM_PARTITIONS
  • KAFKA_MIN_INSYNC_REPLICAS
  • KAFKA_LOG_RETENTION_HOURS
  • KAFKA_LOG_SEGMENT_BYTES
  • KAFKA_LOG_CLEANUP_POLICY

This inconsistency could lead to unpredictable behavior across the cluster.

Apply this diff to add missing configurations:

       KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
       CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"
+      KAFKA_NUM_PARTITIONS: 12
+      KAFKA_MIN_INSYNC_REPLICAS: 2
+      KAFKA_LOG_RETENTION_HOURS: 168
+      KAFKA_LOG_SEGMENT_BYTES: 1073741824
+      KAFKA_LOG_CLEANUP_POLICY: compact
     volumes:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
kafka-3:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka-3
hostname: kafka-3
ports:
- "19094:19094"
environment:
KAFKA_NODE_ID: 3
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,EXTERNAL://0.0.0.0:19094,CONTROLLER://0.0.0.0:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-3:9092,EXTERNAL://localhost:19094
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka-1:9093,2@kafka-2:9093,3@kafka-3:9093
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"
volumes:
- kafka-3-data:/var/lib/kafka/data
networks:
- nerve-exchange-network
healthcheck:
test: ["CMD-SHELL", "kafka-broker-api-versions --bootstrap-server localhost:9092"]
interval: 10s
timeout: 5s
retries: 5
kafka-3:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka-3
hostname: kafka-3
ports:
- "19094:19094"
environment:
KAFKA_NODE_ID: 3
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,EXTERNAL://0.0.0.0:19094,CONTROLLER://0.0.0.0:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-3:9092,EXTERNAL://localhost:19094
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka-1:9093,2@kafka-2:9093,3@kafka-3:9093
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"
KAFKA_NUM_PARTITIONS: 12
KAFKA_MIN_INSYNC_REPLICAS: 2
KAFKA_LOG_RETENTION_HOURS: 168
KAFKA_LOG_SEGMENT_BYTES: 1073741824
KAFKA_LOG_CLEANUP_POLICY: compact
volumes:
- kafka-3-data:/var/lib/kafka/data
networks:
- nerve-exchange-network
healthcheck:
test: ["CMD-SHELL", "kafka-broker-api-versions --bootstrap-server localhost:9092"]
interval: 10s
timeout: 5s
retries: 5
🤖 Prompt for AI Agents
In infra/docker/kafka/docker-compose.yml around lines 76 to 103, kafka-3 is
missing the same environment variables present on kafka-2 (KAFKA_NUM_PARTITIONS,
KAFKA_MIN_INSYNC_REPLICAS, KAFKA_LOG_RETENTION_HOURS, KAFKA_LOG_SEGMENT_BYTES,
KAFKA_LOG_CLEANUP_POLICY); add these exact environment entries under the kafka-3
environment block with the same values used for kafka-2 to ensure consistent
partitioning, ISR and log retention/cleanup behavior across the cluster.

@@ -0,0 +1,34 @@
{
"name": "@repo/kakfa-client",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Fix typo in package name: "kakfa" → "kafka".

The package name contains a typo: @repo/kakfa-client should be @repo/kafka-client. This typo is already propagated to import statements in other files (e.g., apps/order-service/src/controllers/order.controller.ts). Please fix the package name and update all import references throughout the codebase.

Apply this diff:

-  "name": "@repo/kakfa-client",
+  "name": "@repo/kafka-client",

Then update all import statements:


🏁 Script executed:

#!/bin/bash
# Find all files importing the misspelled package name
rg -n '@repo/kakfa-client' --type=ts --type=js --type=json

Length of output: 525


Fix typo in package name: "kakfa" → "kafka" across all files.

The package name contains a typo in packages/kafka-client/package.json (line 2): @repo/kakfa-client should be @repo/kafka-client. This typo is propagated in 5 additional locations:

  • apps/order-worker-service/src/kafka.consumer.ts:6 (import statement)
  • apps/order-service/src/grpc.server.ts:5 (import statement)
  • apps/order-service/src/controllers/order.controller.ts:2 (type import)
  • apps/order-worker-service/package.json:32 (dependency)
  • apps/order-service/package.json:34 (dependency)

Update all 6 occurrences to fix the typo across the codebase.

🤖 Prompt for AI Agents
In packages/kafka-client/package.json around line 2 and the five other locations
listed (apps/order-worker-service/src/kafka.consumer.ts line ~6,
apps/order-service/src/grpc.server.ts line ~5,
apps/order-service/src/controllers/order.controller.ts line ~2,
apps/order-worker-service/package.json line ~32, apps/order-service/package.json
line ~34), fix the typo by renaming the package and all imports/dependency
references from "@repo/kakfa-client" to "@repo/kafka-client". Update the
package.json name field and update any import/type and dependency strings to the
corrected "@repo/kafka-client" value, then run a quick build or TypeScript
typecheck to ensure no remaining references are broken.

Comment on lines +90 to +100
} catch (error) {
this.log.error("Message processing failed", error);
// Optional: send to DLQ topic here
await consumer.commitOffsets([
{
topic,
partition,
offset: (Number(message.offset) + 1).toString(),
},
]);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Committing offsets on error may cause message loss.

Lines 93-99 commit the offset even when message processing fails. This means failed messages are marked as successfully processed and won't be retried, potentially leading to data loss. While the comment mentions sending to a DLQ (Dead Letter Queue), there's no actual DLQ implementation.

Consider one of these approaches:

  1. Don't commit on error (preferred for at-least-once delivery):
         } catch (error) {
           this.log.error("Message processing failed", error);
-          // Optional: send to DLQ topic here
-          await consumer.commitOffsets([
-            {
-              topic,
-              partition,
-              offset: (Number(message.offset) + 1).toString(),
-            },
-          ]);
+          // Optional: implement DLQ here before committing
+          // For now, don't commit to allow retry on next consumer restart
         }
  1. Implement DLQ before committing:
         } catch (error) {
           this.log.error("Message processing failed", error);
-          // Optional: send to DLQ topic here
+          // Send to DLQ topic
+          await this.sendMessage(`${topic}-dlq`, { originalTopic: topic, error: String(error), message: parsed });
           await consumer.commitOffsets([
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
this.log.error("Message processing failed", error);
// Optional: send to DLQ topic here
await consumer.commitOffsets([
{
topic,
partition,
offset: (Number(message.offset) + 1).toString(),
},
]);
}
} catch (error) {
this.log.error("Message processing failed", error);
// Optional: implement DLQ here before committing
// For now, don't commit to allow retry on next consumer restart
}
🤖 Prompt for AI Agents
packages/kafka-client/src/index.ts lines 90-100: the current catch block logs
the error but still commits the message offset which will drop failed messages;
change behavior so we do NOT commit on error (preferred) or, if you need to
acknowledge failures, produce the failed message+metadata to a configured DLQ
topic and await the producer confirmation before committing; specifically,
remove the commitOffsets call from the error path, or implement sending the
original message, error details and headers to a DLQ via the client producer,
await the produce/flush success, then commit the offset only after the DLQ write
succeeds; keep/expand the log to include the error and DLQ outcome and ensure
any producer failures are handled to avoid silent data loss.

Comment on lines +105 to +108
async disconnectConsumer() {
if (this.producer) await this.producer.disconnect();
this.log.info("Consumer disconnected");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical bug: disconnectConsumer disconnects producer instead.

Line 106 calls this.producer.disconnect(), but the method is named disconnectConsumer(). This is incorrect and will fail to disconnect the actual consumer while disconnecting the producer instead.

The current implementation doesn't track consumer instances, so proper consumer disconnection requires refactoring. Consider this approach:

+  private consumers: Consumer[] = [];
+
   async createConsumer(groupId: string): Promise<Consumer> {
     const consumer = this.kafka.consumer({ groupId });
     await consumer.connect();
     this.log.info(`Consumer (${groupId}) connected`);
+    this.consumers.push(consumer);
     return consumer;
   }

   async disconnectConsumer() {
-    if (this.producer) await this.producer.disconnect();
-    this.log.info("Consumer disconnected");
+    await Promise.all(this.consumers.map(c => c.disconnect()));
+    this.consumers = [];
+    this.log.info("All consumers disconnected");
   }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/kafka-client/src/index.ts around lines 105 to 108,
disconnectConsumer incorrectly calls this.producer.disconnect() and there is no
consumer instance being tracked; change the implementation to track the consumer
instance(s) (e.g., add a this.consumer property or a map if multiple consumers),
ensure consumer is assigned when created, and in disconnectConsumer check for
and await this.consumer.disconnect() (or iterate and disconnect all tracked
consumers) then log "Consumer disconnected"; also guard against null/undefined
and handle errors appropriately.

price: z.number().int().positive().optional(),
quantity: z.number().int().positive(),
side: z.enum(["BUY", "SELL"]),
type: z.enum(["MARKET", "LIMIT", "STOP", "STOP_LIMIT"]),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing STOP_MARKET order type in enum.

The validator defines STOP_LIMIT but based on the constants in apps/order-worker-service/src/constants.ts (lines 13-18), STOP_MARKET is also a valid order type. This inconsistency could cause validation failures for valid orders.

Apply this diff to add the missing order type:

-      type: z.enum(["MARKET", "LIMIT", "STOP", "STOP_LIMIT"]),
+      type: z.enum(["MARKET", "LIMIT", "STOP_LIMIT", "STOP_MARKET"]),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/validator/src/Order.ts around line 10, the z.enum for order type is
missing the STOP_MARKET value; update the enum to include "STOP_MARKET"
alongside "MARKET", "LIMIT", "STOP", and "STOP_LIMIT" so it matches the
constants defined in apps/order-worker-service/src/constants.ts and will
validate STOP_MARKET orders correctly.

@sameerkrdev sameerkrdev merged commit 0e81c25 into main Dec 24, 2025
1 check passed
@sameerkrdev sameerkrdev deleted the feat/kafka branch January 3, 2026 19:44
@coderabbitai coderabbitai bot mentioned this pull request Feb 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants