# How to Keep Cursor Aligned with Microservices Architecture

- Tool: Cursor
- Difficulty: Advanced
- Time required: 15-20 min
- Compatibility: Cursor Pro+, any backend language
- Last updated: March 2026

## TL;DR

Keep Cursor aligned with microservices architecture by defining service boundaries in .cursorrules, creating per-service .cursor/rules/ directories that enforce API contracts and isolation, and using @folder context to scope prompts to individual services. Without explicit boundaries, Cursor generates monolithic code that violates service isolation.

## Teaching Cursor About Service Boundaries

Cursor defaults to monolithic patterns: shared databases, direct function calls, and tightly coupled modules. In a microservices architecture, each service must own its data, communicate through APIs or events, and maintain independent deployment. This tutorial shows you how to encode these constraints so Cursor generates code that respects service boundaries, uses proper inter-service communication, and follows your API contract conventions.

## Before you start

- Cursor installed (Pro+ recommended)
- A microservices project with defined service boundaries
- API contract definitions (OpenAPI, protobuf, or similar)
- Understanding of microservices communication patterns

## Step-by-step guide

### 1. Map your service architecture in .cursorrules

Document all services, their responsibilities, and allowed communication paths. This gives Cursor a high-level understanding of your architecture that applies across all prompts.

```
# .cursorrules

## Microservices Architecture
### Services
- user-service (port 3001): auth, profiles, preferences
- order-service (port 3002): orders, cart, checkout
- payment-service (port 3003): payment processing, refunds
- notification-service (port 3004): email, push, SMS

### Rules
- Each service has its OWN database — never share tables
- Services communicate via REST APIs or event bus (RabbitMQ)
- Never import code directly from another service
- API contracts defined in /contracts/{service}.openapi.yml
- Use service client libraries for inter-service calls
- Each service is independently deployable
```

**Expected result:** Cursor understands service boundaries and generates code within the correct service context.

### 2. Create per-service auto-attaching rules

Place .cursor/rules/ in each service directory with service-specific constraints. These rules auto-attach when editing files in that service, ensuring correct database, API, and dependency usage.

```
---
description: Order service boundaries
globs: "services/order-service/**"
alwaysApply: false
---

- Database: orders_db (PostgreSQL) — only access orders, order_items, cart tables
- API base: /api/orders
- Dependencies: @company/user-client for user lookups, @company/payment-client for payments
- NEVER import from services/user-service/ or services/payment-service/ directly
- Publish events: OrderCreated, OrderCancelled, OrderCompleted
- Subscribe to: PaymentCompleted, PaymentFailed
- Port: 3002, configured via ORDER_SERVICE_PORT env var
```

> Pro tip: List the exact database tables each service owns. Cursor will only generate queries for those tables.

**Expected result:** Service-specific rules auto-attach when editing files in the order service.

### 3. Generate inter-service communication code

When generating code that involves multiple services, reference the API contracts and service client libraries. Cursor generates proper HTTP client calls or event publishing instead of direct imports.

```
// Prompt to type in Cursor Composer (Cmd+I):
// @contracts/user-service.openapi.yml @services/order-service/src/clients/
// Generate a function in the order service that:
// 1. Fetches user details from the user service via REST client
// 2. Creates an order in the local orders_db
// 3. Publishes an OrderCreated event to RabbitMQ
// 4. Returns the created order with user name
// Use the existing service client pattern from the clients/ directory.
// Handle: user not found, service unavailable, timeout scenarios.
```

> Pro tip: Always reference the API contract file (@contracts/*.openapi.yml) so Cursor generates correct request/response types.

**Expected result:** Cursor generates code that uses HTTP clients and events for cross-service communication instead of direct imports.

### 4. Generate API contract files with Cursor

Use Composer to generate OpenAPI specifications from your existing service code, or generate service implementations from existing contracts. This keeps contracts and implementations in sync.

```
// Prompt to type in Cursor Composer (Cmd+I):
// @services/order-service/src/routes/
// Generate an OpenAPI 3.1 specification for the order service.
// Include all endpoints from the routes directory.
// For each endpoint: path, method, request body, response schema, error responses.
// Save to contracts/order-service.openapi.yml
```

**Expected result:** A complete OpenAPI specification matching your service's actual endpoints.

## Complete code example

File: `services/order-service/.cursor/rules/boundary.mdc`

```markdown
---
description: Order service microservice boundaries
globs: "services/order-service/**"
alwaysApply: false
---

# Order Service Boundary Rules

## Owned Resources
- Database: orders_db (PostgreSQL)
- Tables: orders, order_items, cart, cart_items
- API path prefix: /api/orders
- Port: 3002 (ORDER_SERVICE_PORT)

## Allowed Dependencies
- Direct: express, pg, zod, amqplib
- Service clients: @company/user-client, @company/payment-client
- Shared: @company/shared-types, @company/logger

## Forbidden
- NEVER import from services/user-service/ or services/payment-service/
- NEVER query tables owned by other services
- NEVER use shared database connections across services

## Inter-Service Communication
- User lookups: UserClient.getUser(userId) — REST call to user-service
- Payment: PaymentClient.createCharge(orderId, amount) — REST call
- Events published: OrderCreated, OrderUpdated, OrderCancelled
- Events consumed: PaymentCompleted, PaymentFailed, UserDeleted

## Error Handling
- Wrap all service client calls in try/catch
- Handle: ServiceUnavailable, Timeout, NotFound
- Use circuit breaker pattern for external service calls
- Return appropriate HTTP status codes (502 for upstream failures)
```

## Common mistakes

- **Letting Cursor import code directly between services** — Direct imports create tight coupling that prevents independent deployment and violates the core microservices principle of service autonomy. Fix: Add explicit FORBIDDEN import rules per service and require service client libraries for cross-service communication.
- **Not defining database ownership per service** — Without clear table ownership, Cursor generates shared database queries that create hidden coupling between services. Fix: List the exact tables each service owns in its .cursor/rules/ file.
- **Generating synchronous cross-service calls for all communication** — Synchronous REST calls between services create cascading failures. Some operations should use async events. Fix: Define which operations use REST (queries) vs events (state changes) in your service rules.

## Best practices

- Map all services, their databases, and communication paths in .cursorrules
- Create per-service .cursor/rules/ with owned resources and forbidden imports
- Reference API contract files when generating inter-service communication code
- Use @folder scoped to individual services to prevent cross-service context
- Define event-driven vs synchronous communication patterns per operation type
- Generate API contracts from code and code from contracts to keep them in sync
- Handle service unavailability (timeout, circuit breaker) in all cross-service calls

## Frequently asked questions

### How do I prevent Cursor from generating monolithic code?

Define service boundaries explicitly in .cursorrules with owned resources, allowed dependencies, and forbidden imports. Create per-service .cursor/rules/ and always scope prompts with @folder to the specific service.

### Can Cursor generate API contracts between services?

Yes. Reference your service routes with @file and ask Cursor to generate an OpenAPI specification. Or provide an existing contract and ask Cursor to generate a service client library from it.

### Should I open separate Cursor windows per service?

Yes, for large microservice projects. Each window indexes only that service's code, improving performance and ensuring the AI stays within service boundaries.

### How does Cursor handle shared code in microservices?

Create a shared packages directory (e.g., packages/shared-types) and list it as an allowed import in every service's rules. Only put truly cross-cutting types and utilities in shared packages.

### Can Cursor generate Docker Compose for microservices?

Yes. Reference all service Dockerfiles and the service architecture from .cursorrules. Prompt Cursor to generate a docker-compose.yml with separate containers, networking, and health checks per service.

---

Source: https://www.rapidevelopers.com/cursor-tutorial/how-to-ensure-cursor-ai-s-suggested-code-follows-a-microservices-architecture-pattern
© RapidDev — https://www.rapidevelopers.com/cursor-tutorial/how-to-ensure-cursor-ai-s-suggested-code-follows-a-microservices-architecture-pattern
