Catalog en serio: kinds, relaciones y providers

Objetivos del capítulo
  • Modelar cinco entidades reales: web, API, worker, base de datos y System.

  • Definir relaciones semánticas entre componentes (dependsOn, providesApi, consumesApi).

  • Conectar un provider de descubrimiento desde GitHub.

  • Importar proyectos ya existentes en bloque.

En el :cap-03 cargamos una entidad. Pero una IDP vive o muere según su catálogo: si tiene 5 componentes suecos, no es una IDP, es un monolito. La salsa está en modelar correctamente un conjunto de servicios, sus relaciones y cómo se sincronizan desde las fuentes de verdad.

Ahora poblamos el catálogo con los cinco componentes base de Sazón Foods, definimos cómo se relacionan y los importamos desde GitHub.

Los cinco componentes semilla

Una SaaS B2B de restaurantes tiene, en su forma más simple:

  • Una web (panel para cocineros).

  • Una API que sirve la lógica de negocio.

  • Un worker que ejecuta tareas async (notificaciones, informes).

  • Una base de datos Postgres donde vive el estado.

  • Un System que agrupa los anteriores como una unidad lógica.

Diagram
Figure 7. Los cinco componentes y su System

El System es la mise-en-place agrupada: la bandeja con los cinco ingredientes que componen el plato sazon-restaurant. Sin esa bandeja, el cocinero tiene que ir al mercado cada vez.

El catálogo semilla:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: sazon-web
  description: Frontend público
  tags: [frontend, customer-facing]
spec:
  type: website
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
---
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: sazon-api
  description: API REST del menú
  tags: [backend, nodejs]
spec:
  type: service
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
  providesApis: [sazon-menu-api]
---
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: sazon-worker
  description: Worker de pedidos
  tags: [backend, worker]
spec:
  type: service
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
---
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
  name: sazon-postgres-prod
  description: Postgres de producción
spec:
  type: database
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
---
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: sazon-restaurant
  description: Sistema SaaS B2B de Sazón Foods
spec:
  owner: team-cocina
  domain: restaurant
Qué acabas de hacer
  • Cinco entidades del kind Component y Resource, todas con spec.owner, spec.system apuntando a sazon-restaurant.

  • Una entidad System (lo crearemos a continuación) que las agrupa.

  • El namespace default es la convención por defecto; en producción usa el dominio de tu empresa (ej. acme, sazon-foods).

La entidad System

El System agrupa componentes que comparten dominio. En Backstage se modela como una entidad con kind System. Sus relaciones hasPart lo enlazan con cada componente.

apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: sazon-restaurant
  title: Sazón Foods — Restaurante SaaS
  description: Plataforma SaaS B2B para gestión de pedidos en restaurantes.
spec:
  owner: guests
  domain: sazon-foods
  type: saas

Tras guardar el archivo y reiniciar el backend, refresca el catalog: sazon-restaurant aparece como padre de los cinco componentes.

Backstage soporta relaciones entre entidades. Tres relaciones nos llevan lejos:

  • dependsOn: A depende de B (ej. la API depende de la DB).

  • providesApi: A expone una API llamada X (ej. la API provee sazon-orders).

  • consumesApi: A consume una API llamada X (ej. la web consume sazon-orders).

Por qué importan las relaciones

Las relaciones permiten a los plugins renderizar grafos de dependencias (Tech Insights, Catalog Graph) y al Scaffolder razonar sobre qué generar. Una entidad sin relaciones es un nodo aislado en un grafo.

Definimos las relaciones como un YAML de relaciones:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: sazon-frontend
  description: SPA React del portal
  tags: [frontend]
spec:
  type: website
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
  consumesApis: [sazon-menu-api]
---
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
  name: sazon-menu-api
  description: API pública del menú
  tags: [rest]
spec:
  type: openapi
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
  definition: |
    openapi: 3.0.0
    info: { title: sazon-menu-api, version: 1.0.0 }
    paths:
      /menu: { get: { summary: Lista el menú } }
---
# sazon-api depende de sazon-postgres-prod
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: sazon-api
  description: API REST del menú
  tags: [backend, nodejs]
spec:
  type: service
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
  providesApis: [sazon-menu-api]
  dependsOn: [resource:sazon-restaurant/sazon-postgres-prod]
---
# sazon-worker consume sazon-api y depende de la misma DB
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: sazon-worker
  description: Worker de pedidos
  tags: [backend, worker]
spec:
  type: service
  lifecycle: production
  owner: team-cocina
  system: sazon-restaurant
  consumesApis: [sazon-menu-api]
  dependsOn: [resource:sazon-restaurant/sazon-postgres-prod]

Cada relación tiene un source, type y target. La entidad API definida aparte (con metadata.name: sazon-orders-api y spec.type: openapi) es referenciada por las relaciones providesApi/consumesApi.

Providers: discovery automático desde GitHub

Mantener entidades a mano es tedioso. Backstage soporta providers que descubren repos en GitHub y los ingieren como entidades automáticamente.

¿Cómo funciona?

El GithubDiscoveryProcessor escanea una org o un equipo, busca catalog-info.yaml en cada repo, y los ingesta como entidades. Se ejecuta en cada tick configurable.

Activamos el provider desde app-config.addons.yaml:

catalog:
  providers:
    github:
      providerId:
        - id: provider-discovery
          github: https://github.com
          orgs: ['sazon-foods']
          schedule:
            initialDelay: { seconds: 30 }
            frequency: { minutes: 30 }
          filters:
            repository: ^sazon-.*$
          producer:
            getCodeowners: true
Política de filtros

El filters es clave en producción: sin él, el provider ingesta cualquier repo de la org, incluidos los personales o archivados. Acota el repo:.* y topic:backstage para ingerir solo lo declarado.

Importar proyectos existentes en bloque

Si tienes decenas de repos ya creados y quieres migrarlos de golpe, hay un script de bulk-import. Backstage lo publica como ejemplo y lo hemos adaptado para Sazón Foods:

set -euo pipefail

# Configura el token en app-config.local.yaml o via GITHUB_TOKEN.
GITHUB_TOKEN="${GITHUB_TOKEN:-}"
ORG="${CATALOG_ORG:-sazon-foods}"

# Endpoint exacto del bulk import (cap-04, RQ-A22-bulk-import-endpoint).
ENDPOINT="http://localhost:7007/api/catalog/import-many"

curl -X POST "$ENDPOINT" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"items\": [
      { \"type\": \"repository\", \"target\": \"$ORG/sazon-web\" },
      { \"type\": \"repository\", \"target\": \"$ORG/sazon-api\" },
      { \"type\": \"repository\", \"target\": \"$ORG/sazon-worker\" }
    ]
  }"

echo "==> PRs creadas en github.com/$ORG. Revisa y mergea."
El flujo del script
  1. Clona cada repo listado en repos.txt.

  2. Si encuentra un catalog-info.yaml, lo deja intacto.

  3. Si no, lo genera con heurísticas (lenguaje detectado, nombre del repo, owner).

  4. Crea un PR en cada repo con el nuevo catalog-info.yaml.

Útil cuando arrancas con muchos servicios ya en producción.

Example 4. Receta del capítulo
  1. Crea los cinco Component y un System.

  2. Añade relaciones dependsOn, providesApi y consumesApi.

  3. Activa el GithubDiscoveryProcessor con un filtro razonable.

  4. Si tienes repos legacy, ejecuta el bulk-import y revisa los PRs.

Resumen

  • Modelar System + Component + relaciones es la base del catalog.

  • Las relaciones permiten grafos, dependencias y razonamiento sobre la topología.

  • El GithubDiscoveryProcessor sincroniza el catalog desde GitHub con filtros.

  • El bulk-import acelera la adopción en empresas con muchos servicios.

Glosario del capítulo

kind

Categoría de entidad (Component, API, Resource, System, Domain, Group, User).

relación

Edge del grafo: dependsOn, providesApi, consumesApi, hasPart, ownerOf.

provider

Componente que descubre entidades en una fuente externa (GitHub, GitLab, etc.).

processor

Pieza que procesa una entidad: enriquece, valida, transforma.

Próximo capítulo

El backend: en qué cocina se cocina —pasamos del catalog al backend: arquitectura, configuración y cómo extenderlo con plugins.

Parte II: Parte 2 — Backend: la cocina por dentro

La New Backend System, los services del core, y nuestro primer plugin backend completo. Aquí es donde la cocina pasa de hobby a restaurante.