๐๏ธ Arquitectura de Microservicios
campaign-service
Consumidor
Puerto 3000
GET /api/leads/{id}
โ contrato firmado
leads-service
Proveedor
Puerto 3456
GET /api/metrics/campaign/{id}
โ contrato firmado
analytics-service
Proveedor
Puerto 3789
8
Contratos activos
100%
Tests pasando
3
Servicios monitoreados
v2.4.1
Ultimo deploy verificado
๐ Flujo Consumer-Driven Contract Testing
paso 1
โ๏ธ
Define expectativas
campaign-svc escribe lo que espera de leads-svc
paso 2
๐
Genera Pact File
Tests de consumidor crean el JSON de contrato
paso 3
๐ค
Publica en Broker
Pact Broker almacena y versiona los contratos
paso 4
โ
Verifica proveedor
leads-svc descarga y valida todos los contratos
paso 5
๐
Can-I-Deploy
CI/CD consulta si es seguro desplegar en prod
Consumer Test
Provider Verification
Pact Broker Config
GitHub Actions CI
Test de Consumidor (campaign-service) tests/leads.consumer.pact.test.ts
// tests/leads.consumer.pact.test.ts // campaign-service define sus expectativas sobre leads-service. import { PactV3, MatchersV3 } from '@pact-foundation/pact'; import { resolve } from 'path'; import { LeadsApiClient } from '../src/api/leadsClient'; const { like, eachLike, string, integer, decimal } = MatchersV3; const provider = new PactV3({ consumer: 'campaign-service', provider: 'leads-service', dir: resolve(__dirname, '../pacts'), }); describe('Leads API โ Consumer Tests', () => { it('obtiene un lead por ID con todos sus campos', async () => { await provider .given('existe un lead con ID lead-001') .uponReceiving('peticion de lead por ID') .withRequest({ method: 'GET', path: '/api/leads/lead-001', headers: { Accept: 'application/json', Authorization: string('Bearer token') }, }) .willRespondWith({ status: 200, headers: { 'Content-Type': 'application/json' }, body: like({ id: string('lead-001'), name: string('Maria Garcia'), email: string('maria@empresa.com'), score: decimal(87.5), status: string('qualified'), tags: eachLike(string('newsletter')), campaign: string('camp-spring-2026'), }), }) .executeTest(async (mockServer) => { const client = new LeadsApiClient(mockServer.url); const lead = await client.getLead('lead-001'); expect(lead.id).toBe('lead-001'); expect(lead.score).toBeGreaterThan(0); expect(lead.status).toBeDefined(); }); }); it('lista leads de una campana con paginacion', async () => { await provider .given('existen 3 leads para camp-spring-2026') .uponReceiving('peticion de leads por campana') .withRequest({ method: 'GET', path: '/api/leads', query: { campaign: 'camp-spring-2026', page: '1' }, }) .willRespondWith({ status: 200, body: like({ data: eachLike(like({ id: string(), name: string(), score: decimal() })), total: integer(3), page: integer(1), }), }) .executeTest(async (mockServer) => { const client = new LeadsApiClient(mockServer.url); const resp = await client.getLeadsByCampaign('camp-spring-2026'); expect(resp.data.length).toBeGreaterThan(0); expect(resp.total).toBeDefined(); }); }); it('anade etiqueta a un lead existente', async () => { await provider .given('existe un lead con ID lead-001') .uponReceiving('peticion para anadir tag nurturing') .withRequest({ method: 'POST', path: '/api/leads/lead-001/tag', body: { tag: 'nurturing' }, }) .willRespondWith({ status: 200, body: like({ success: true, tags: eachLike(string()) }), }) .executeTest(async (mockServer) => { const client = new LeadsApiClient(mockServer.url); const res = await client.addTag('lead-001', 'nurturing'); expect(res.success).toBe(true); }); }); });
Verificacion del Proveedor (leads-service) tests/leads.provider.pact.test.ts
// tests/leads.provider.pact.test.ts // leads-service verifica que cumple todos los contratos publicados en el broker. import { Verifier } from '@pact-foundation/pact'; import { startApp } from '../src/app'; import { seedLead, clearLeads } from '../test-utils/database'; describe('Leads Service โ Provider Verification', () => { let server: any; beforeAll(async () => { server = await startApp(3456); }); afterAll(async () => { await server.close(); }); it('cumple todos los contratos del Pact Broker', async () => { const verifier = new Verifier({ providerBaseUrl: 'http://localhost:3456', provider: 'leads-service', pactBrokerUrl: process.env.PACT_BROKER_URL, pactBrokerToken: process.env.PACT_BROKER_TOKEN, publishVerificationResult: process.env.CI === 'true', providerVersion: process.env.GIT_COMMIT, providerVersionBranch: process.env.GIT_BRANCH, consumerVersionSelectors: [ { mainBranch: true }, { deployedOrReleased: true }, ], // Estado de la base de datos antes de cada interaccion stateHandlers: { 'existe un lead con ID lead-001': async () => { await seedLead({ id: 'lead-001', name: 'Maria Garcia', email: 'maria@empresa.com', score: 87.5, status: 'qualified', tags: ['newsletter', 'webinar'], campaign: 'camp-spring-2026', }); }, 'existen 3 leads para camp-spring-2026': async () => { await seedLead({ id: 'lead-001', campaign: 'camp-spring-2026', score: 87.5 }); await seedLead({ id: 'lead-002', campaign: 'camp-spring-2026', score: 62.0 }); await seedLead({ id: 'lead-003', campaign: 'camp-spring-2026', score: 45.3 }); }, }, }); await verifier.verifyProvider(); }); });
Matriz de Contratos
Participantes
Entornos
Webhooks
๐ Matriz de Verificacion de Contratos
| Consumidor | Proveedor | Interaccion | Version consumidor | Version proveedor | Resultado | Fecha |
|---|---|---|---|---|---|---|
| campaign-service | leads-service | GET /api/leads/{id} | 2.4.1 / main | 3.1.0 / main | PASS | 2026-06-16 09:14 |
| campaign-service | leads-service | GET /api/leads?campaign={id} | 2.4.1 / main | 3.1.0 / main | PASS | 2026-06-16 09:14 |
| campaign-service | leads-service | POST /api/leads/{id}/tag | 2.4.1 / main | 3.1.0 / main | PASS | 2026-06-16 09:14 |
| campaign-service | analytics-service | GET /api/metrics/campaign/{id} | 2.4.1 / main | 1.8.2 / main | PASS | 2026-06-16 09:15 |
| campaign-service | analytics-service | GET /api/metrics/campaign/{id}/breakdown | 2.4.1 / main | 1.8.2 / main | PASS | 2026-06-16 09:15 |
โ๏ธ Pipeline GitHub Actions โ Ultimo Run (main @ 2.4.1)
Checkout & Install
โ 34s
โ
Consumer Tests
โ 12s
โ
Publish Pacts
โ 4s
โ
Can-I-Deploy
โ 3s
โ
Deploy Prod
โ 58s
$ npx pact-broker can-i-deploy \
--pacticipant campaign-service \
--version a3f8d21c \
--to-environment production \
--broker-base-url $PACT_BROKER_URL \
--broker-token $PACT_BROKER_TOKEN
INFO Checking if campaign-service@a3f8d21c can be deployed to production
INFO Loading matrix for campaign-service, latest version and its dependencies
CONSUMER PROVIDER STATUS
โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ โโโโโโ
campaign-service leads-service โ verified
campaign-service analytics-service โ verified
โ Computer says yes \o/
campaign-service v2.4.1 (a3f8d21c) can be deployed to production.
All contracts satisfied.
$ npx pact-broker record-deployment \
--pacticipant campaign-service --version a3f8d21c \
--environment production
โ Recorded deployment of campaign-service v2.4.1 to production
GitHub Actions โ Pipeline completo .github/workflows/pact-consumer.yml
# .github/workflows/pact-consumer.yml # Pipeline de campaign-service: tests de contrato + deploy seguro a produccion. name: Consumer Contract Tests โ campaign-service on: push: branches: [main, 'feature/**'] pull_request: branches: [main] env: PACT_BROKER_URL: ${{ secrets.PACT_BROKER_URL }} PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }} GIT_COMMIT: ${{ github.sha }} GIT_BRANCH: ${{ github.ref_name }} jobs: pact-consumer: name: Contract Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: npm - name: Instalar dependencias run: npm ci - name: Ejecutar consumer tests (genera pacts/) run: npm test -- --testPathPattern="\.pact\.test\." - name: Publicar contratos en Pact Broker run: | npx pact-broker publish pacts/ \ --consumer-app-version $GIT_COMMIT \ --branch $GIT_BRANCH - name: Can I deploy? (verifica contratos satisfechos) run: | npx pact-broker can-i-deploy \ --pacticipant campaign-service \ --version $GIT_COMMIT \ --to-environment production deploy: name: Deploy a Produccion needs: pact-consumer if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - name: Deploy run: ./scripts/deploy.sh production - name: Registrar deploy en Pact Broker run: | npx pact-broker record-deployment \ --pacticipant campaign-service \ --version $GIT_COMMIT \ --environment production