Blog
Node.jsMicroservicesTypeScriptArchitecture

Building Scalable Microservices with Node.js

·8 min read

Building Scalable Microservices with Node.js

When building large-scale applications, microservices architecture offers significant advantages in terms of scalability, maintainability, and team autonomy. In this article, I'll share my experience building microservices at Grupo Barigui, where we serve over 100 retail stores.

Why Microservices?

Traditional monolithic applications can become difficult to maintain as they grow. Microservices solve this by:

  • Independent Deployment: Each service can be deployed independently
  • Technology Flexibility: Different services can use different technologies
  • Scalability: Scale only the services that need it
  • Team Autonomy: Teams can work independently on different services

Setting Up the Architecture

Here's how we structure our Node.js microservices:

// service/src/index.ts
import express from 'express';
import { connectToMessageQueue } from './messaging';
import { healthRouter } from './routes/health';
import { ordersRouter } from './routes/orders';

const app = express();

app.use(express.json());
app.use('/health', healthRouter);
app.use('/api/orders', ordersRouter);

async function bootstrap() {
  await connectToMessageQueue();
  
  app.listen(process.env.PORT || 3000, () => {
    console.log('Order service is running');
  });
}

bootstrap();

Message Queue Integration

For inter-service communication, we use RabbitMQ:

// messaging/publisher.ts
import amqp from 'amqplib';

export async function publishEvent(
  exchange: string,
  routingKey: string,
  message: object
) {
  const connection = await amqp.connect(process.env.RABBITMQ_URL);
  const channel = await connection.createChannel();
  
  await channel.assertExchange(exchange, 'topic', { durable: true });
  
  channel.publish(
    exchange,
    routingKey,
    Buffer.from(JSON.stringify(message)),
    { persistent: true }
  );
}

Containerization with Docker

Each service is containerized for consistent deployment:

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

Key Takeaways

1. Start with clear service boundaries 2. Implement proper health checks 3. Use asynchronous communication when possible 4. Monitor everything with proper observability 5. Design for failure

Microservices aren't a silver bullet, but when implemented correctly, they can significantly improve your system's scalability and maintainability.