---
id: "ADR-001"
title: "API-first con Protocol Buffers y buf"
date: "2026-04-12"
status: "active"
superseded_by: ""
tags: ["api", "protobuf", "buf", "grpc", "contract", "codegen"]
summary: "En el contexto de los microservicios White Label que exponen APIs gRPC/HTTP,
frente a la necesidad de mantener una fuente de verdad única para el contrato del servicio
y eliminar mapeos entre capas, decidimos adoptar un modelo API-first donde el archivo .proto
es la fuente de verdad, buf genera automáticamente el código gRPC, el HTTP gateway y la
documentación Swagger, y los structs Protobuf son el modelo del dominio, para lograr
consistencia total entre contrato y código, aceptando que el .proto debe existir antes
de comenzar cualquier implementación."
---

## Contexto

Un microservicio White Label expone una API gRPC con un HTTP gateway. Sin una convención
sobre cómo se define y mantiene esa API, cada equipo toma decisiones distintas: algunos
definen el .proto como formalización posterior, otros crean entidades de dominio propias
y las mapean al .proto, otros editan el código generado directamente.

El resultado es fricción en tres dimensiones: inconsistencia entre el contrato declarado
y el comportamiento real, proliferación de mapeos entre tipos, y dificultad para regenerar
el código sin perder cambios manuales.

La pregunta de diseño no es si usar Protobuf (está decidido) sino cómo integrar el
ciclo de vida del .proto con el del código del servicio.

## Alternativas consideradas

### Opción A — Protobuf como formalización posterior
Implementar primero en Go y generar el .proto después, o mantenerlos sincronizados manualmente.

**Por qué se descartó:** Cuando el .proto sigue al código, el contrato refleja la
implementación y no las necesidades del consumidor. El HTTP gateway y la documentación
quedan siempre desfasados. La sincronización manual es error-prone.

### Opción B — Entidades de dominio propias + mapeo a Protobuf
Definir structs Go propios para el dominio y mapear hacia/desde los mensajes Protobuf
en el Service layer.

**Por qué se descartó:** Introduce una categoría entera de código sin valor: mapeos
bidireccionales entre representaciones del mismo dato que pueden divergir. Duplica
modelos sin beneficio. El principio de la plataforma es claridad sobre abstracción.

### Opción C — Editar código generado en gen/
Usar la generación como base y ajustar el código en `gen/` manualmente para casos edge.

**Por qué se descartó:** Al regenerar con `buf generate`, los cambios manuales se pierden.
Lleva inevitablemente a no regenerar (porque "rompería cosas"), lo que hace que el código
generado y el .proto diverjan progresivamente.

### Opción elegida — API-first: .proto como fuente de verdad, gen/ sagrado
El .proto define el contrato. buf genera todo lo derivado. El código generado nunca
se toca. Los structs Protobuf son el modelo del dominio en todo el servicio.

## Decisión

We will adopt an API-first model where the `.proto` file is the single source of truth
for the service contract.

### El .proto define el contrato

Todo feature nuevo empieza por el `.proto`. El contrato se define antes de escribir
lógica de negocio. Si el contrato no está definido, el feature no empieza.

```proto
service CartService {
  rpc GetCart(GetCartRequest) returns (GetCartResponse) {
    option (google.api.http) = {
      get: "/v1/cart"
    };
  }
}
```

### buf genera automáticamente

Usando [buf](https://buf.build/), una sola definición genera:

- La interfaz gRPC que el microservicio debe implementar.
- El HTTP gateway que expone la misma API como REST.
- La documentación Swagger del endpoint.
- Los tipos Go que representan request y response.

```bash
make generate   # ← ejecuta buf generate
```

### gen/ nunca se edita

El código generado vive en `gen/` y **nunca se modifica a mano**, bajo ninguna circunstancia.
Editar `gen/` es equivalente a romper la capacidad de regenerar. Si se necesita cambiar
el comportamiento, se cambia el `.proto` y se regenera.

```
gen/
  <proto-package>/    ← código generado por buf — NUNCA editar
```

### Protobuf como modelo del dominio

Los structs generados por Protobuf son el modelo del dominio en todo el servicio.
El Core opera directamente sobre esos mensajes: recibe requests y devuelve responses
generados a partir del `.proto`.

Esto elimina una categoría entera de código: no hay mapeos entre entidades de dominio
y DTOs, no hay transformaciones intermedias, no hay riesgo de que dos representaciones
del mismo dato diverjan.

Agregar entidades de dominio propias paralelas al modelo Protobuf requiere justificación
explícita documentada como ADR.

### Flujo de trabajo buf

```bash
make generate    # regenerar código desde .proto
buf lint         # verificar que el .proto cumple convenciones
buf format -w    # formatear el .proto
buf dep update   # actualizar dependencias del .proto
```

El archivo `buf.gen.yaml` define los plugins y salidas de generación. No se modifica
salvo que se necesite agregar un nuevo tipo de output (requiere ADR).

## Consecuencias

**Positivas:**
- El contrato es la fuente de verdad: lo que está en el .proto es lo que el servicio
  hace, sin excepciones.
- Sin mapeos entre tipos: el código es más simple, más pequeño y menos frágil.
- La documentación Swagger está siempre actualizada con el contrato real.
- Los consumidores del servicio pueden confiar en el .proto sin inspeccionar el código.

**Negativas:**
- Ningún feature puede avanzar sin tener primero el .proto definido y aprobado.
  Esto puede sentirse como fricción cuando el diseño de la API es experimental.
- Cambios en el .proto requieren regeneración y actualización de todos los puntos
  donde se usan los tipos afectados.

**Riesgos y mitigaciones:**
- **Modificar gen/ manualmente** → La regla es absoluta: si se detecta código editado
  en `gen/`, debe reemplazarse por el resultado de `buf generate`. El agente no debe
  editar `gen/` bajo ninguna circunstancia.
- **Features que avanzan sin .proto** → El agente debe detenerse si se le pide implementar
  un endpoint que no tiene .proto definido. Crear un AgDR si el dev insiste sin .proto.

## ADRs relacionados

- ADR-000: Arquitectura en cuatro capas (Service implementa la interfaz generada por Protobuf;
  Core recibe y devuelve mensajes Protobuf directamente)
