Skip to content
Back to skills

Tpl Fullstack Microservices Docker

ASecurity

Template do pack (fullstack/03-microservices-docker.md). Orienta o agente em stacks fullstack e arquitetura ponta a ponta alinhado a esse contexto.

  • 11 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 8, 2026
databasestypescriptgoshellsqlnodeexpressdockertestingapidatabase

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add artubss/SKILLS-CLAUDE-CODE --skill tpl-fullstack-microservices-docker --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Tpl Fullstack Microservices Docker?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Tpl Fullstack Microservices Docker
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/artubss-tpl-fullstack-microservices-docker/badge)](https://www.skillsdirectory.com/skills/artubss-tpl-fullstack-microservices-docker)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: tpl-fullstack-microservices-docker
description: Template do pack (fullstack/03-microservices-docker.md). Orienta o agente em stacks fullstack e arquitetura ponta a ponta alinhado a esse contexto.
metadata:
  version: 1.0.0
  source_template: fullstack/03-microservices-docker.md
  generated_by: install_pack_templates_as_claude_skills
---

# PROJECT: Microservices Architecture (Docker Compose)

Skill gerado a partir do pack `templates-claude-code`. Arquivo de origem: `fullstack/03-microservices-docker.md`. Use como baseline e adapte ao projeto antes de mudancas grandes.

## Conteudo do template

## STACK
- **Orchestration:** Docker Compose v3.9
- **Services:** Node.js 22 + TypeScript per service
- **Queues:** BullMQ + Redis 7
- **API Gateway:** nginx (upstream routing)
- **Database:** PostgreSQL 16 (one per service — DB per service pattern)
- **Auth service:** Shared JWT validation (JWKS endpoint)
- **Tracing:** OpenTelemetry SDK + correlation IDs
- **Testing:** Vitest (unit), Docker Compose (integration)

---

## PROJECT STRUCTURE
```
services/
├── gateway/                     # nginx config + docker
├── auth-service/                # JWT issuance, user identity
│   ├── src/
│   ├── Dockerfile
│   └── package.json
├── user-service/                # User profiles, preferences
│   ├── src/
│   ├── Dockerfile
│   └── package.json
├── post-service/                # Posts CRUD
│   ├── src/
│   ├── Dockerfile
│   └── package.json
└── notification-service/        # Email/push via BullMQ consumer
    ├── src/
    ├── Dockerfile
    └── package.json
shared/
├── types/                       # Shared TypeScript interfaces
└── events/                      # Event schema definitions
docker-compose.yml
docker-compose.override.yml      # Dev overrides (volumes, ports)
docker-compose.prod.yml
.env.example
```

---

## ARCHITECTURE RULES
1. **Database per service** — each service owns its PostgreSQL schema; no shared DB access.
2. **Async events for cross-service side effects** — use BullMQ for email, notifications, analytics; never synchronous HTTP chains.
3. **Correlation ID on every request** — gateway injects `X-Correlation-ID`; all services propagate it in every log and outgoing request.
4. **Services communicate via HTTP or events only** — no shared memory, no shared filesystem mounts.
5. **Health check endpoint required** — every service exposes `GET /health` returning `{ status: 'ok', version, db: 'connected' }`.
6. **Environment parity** — prod uses identical Dockerfile to dev; only env vars differ.
7. **Secrets via environment variables** — never bake secrets into images; use `.env` (dev) or Docker Secrets/Vault (prod).

---

## DOCKER COMPOSE CONFIGURATION

```yaml
# docker-compose.yml
version: '3.9'

x-service-defaults: &service-defaults
  restart: unless-stopped
  networks: [app-net]
  logging:
    driver: json-file
    options: { max-size: "10m", max-file: "3" }

services:
  gateway:
    <<: *service-defaults
    image: nginx:1.25-alpine
    ports: ["80:80"]
    volumes: ["./services/gateway/nginx.conf:/etc/nginx/nginx.conf:ro"]
    depends_on: [auth-service, user-service, post-service]

  auth-service:
    <<: *service-defaults
    build: { context: ./services/auth-service, target: production }
    environment:
      DATABASE_URL: postgres://auth:secret@auth-db:5432/auth
      JWT_SECRET:   ${JWT_SECRET}
      REDIS_URL:    redis://redis:6379
    depends_on: { auth-db: { condition: service_healthy }, redis: { condition: service_healthy } }
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3001/health"]
      interval: 10s
      timeout: 5s
      retries: 3

  auth-db:
    image: postgres:16-alpine
    environment: { POSTGRES_USER: auth, POSTGRES_PASSWORD: secret, POSTGRES_DB: auth }
    volumes: [auth-db-data:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U auth"]
      interval: 5s

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
    volumes: [redis-data:/data]

  notification-service:
    <<: *service-defaults
    build: { context: ./services/notification-service, target: production }
    environment:
      REDIS_URL:   redis://redis:6379
      SMTP_HOST:   ${SMTP_HOST}
    depends_on: { redis: { condition: service_healthy } }

networks:
  app-net:

volumes:
  auth-db-data:
  redis-data:
```

---

## CORRELATION ID PROPAGATION

```typescript
// shared/middleware/correlation.ts
import { randomUUID } from 'node:crypto'
import type { Request, Response, NextFunction } from 'express'

export const CORRELATION_HEADER = 'x-correlation-id'

export function correlationMiddleware(req: Request, res: Response, next: NextFunction): void {
  const correlationId = (req.headers[CORRELATION_HEADER] as string) ?? randomUUID()
  req.correlationId = correlationId
  res.setHeader(CORRELATION_HEADER, correlationId)
  next()
}

// Outgoing HTTP call — propagate correlation ID
export function makeRequest(
  url: string,
  init: RequestInit,
  correlationId: string,
): Promise<Response> {
  return fetch(url, {
    ...init,
    headers: { ...init.headers, [CORRELATION_HEADER]: correlationId },
  })
}

declare global { namespace Express { interface Request { correlationId: string } } }
```

---

## BULLMQ EVENT-DRIVEN PATTERN

```typescript
// shared/events/index.ts
export type AppEvent =
  | { type: 'USER_CREATED'; payload: { userId: string; email: string; name: string } }
  | { type: 'POST_PUBLISHED'; payload: { postId: string; authorId: string; title: string } }

// services/user-service/src/producers/user.ts
import { Queue } from 'bullmq'
import Redis     from 'ioredis'
import type { AppEvent } from '../../../shared/events'

const connection = new Redis(process.env.REDIS_URL!, { maxRetriesPerRequest: null })
const eventQueue = new Queue<AppEvent>('app-events', { connection })

export async function emitUserCreated(userId: string, email: string, name: string): Promise<void> {
  await eventQueue.add('USER_CREATED', {
    type: 'USER_CREATED',
    payload: { userId, email, name },
  }, { attempts: 3, backoff: { type: 'exponential', delay: 1000 } })
}

// services/notification-service/src/consumers/events.ts
import { Worker } from 'bullmq'
import Redis      from 'ioredis'
import type { AppEvent } from '../../../shared/events'

const connection = new Redis(process.env.REDIS_URL!, { maxRetriesPerRequest: null })

new Worker<AppEvent>('app-events', async (job) => {
  const { type, payload } = job.data
  if (type === 'USER_CREATED') {
    await sendWelcomeEmail(payload.email, payload.name)
  }
}, { connection, concurrency: 5 })
```

---

## NGINX GATEWAY

```nginx
# services/gateway/nginx.conf
upstream auth    { server auth-service:3001; }
upstream users   { server user-service:3002; }
upstream posts   { server post-service:3003; }

server {
  listen 80;
  add_header X-Request-ID $request_id always;

  location /api/auth/   { proxy_pass http://auth/; proxy_set_header X-Correlation-ID $request_id; }
  location /api/users/  { proxy_pass http://users/; proxy_set_header X-Correlation-ID $request_id; }
  location /api/posts/  { proxy_pass http://posts/; proxy_set_header X-Correlation-ID $request_id; }
  location /health      { return 200 '{"status":"ok"}'; add_header Content-Type application/json; }
}
```

---

## ROUTING TABLE

| Method | Path (gateway) | Target Service | Action |
|--------|---------------|----------------|--------|
| POST | /api/auth/login | auth-service | Issue JWT |
| POST | /api/auth/register | auth-service | Register user |
| GET | /api/users/:id | user-service | Get profile |
| PUT | /api/users/:id | user-service | Update profile |
| GET | /api/posts | post-service | List posts |
| POST | /api/posts | post-service | Create post |
| DELETE | /api/posts/:id | post-service | Delete post |
| GET | /health | nginx local | Gateway health |
| BullMQ | USER_CREATED | notification-service | Send welcome email |
| BullMQ | POST_PUBLISHED | notification-service | Notify followers |

---

## QUALITY GATES
- [ ] `docker compose up --build` — all services healthy
- [ ] All `GET /health` endpoints return `{ status: 'ok' }` with HTTP 200
- [ ] Correlation IDs appear in logs for every request across services
- [ ] BullMQ workers restart automatically on crash (Docker `restart: unless-stopped`)
- [ ] No service accesses another service's PostgreSQL database
- [ ] `docker compose down -v && docker compose up` — clean state works
- [ ] Secrets in `.env.example` marked clearly (never real values committed)
- [ ] `vitest run` passes all unit tests per service

---

## FORBIDDEN
- ❌ Shared database between services — strict DB-per-service
- ❌ Synchronous HTTP chain A→B→C→D — use events for side effects
- ❌ Hardcoded IPs/hostnames — use Docker service names
- ❌ Root user in Dockerfiles — always `USER node` or `USER 1001`
- ❌ `latest` Docker image tags in production compose files — pin versions
- ❌ Missing healthchecks — every service must have one
- ❌ `console.log` without correlation ID context
- ❌ Fire-and-forget BullMQ jobs without error handling / deadletter queue

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…