Una API puede conceptualizarse como un módulo#
Para diseñar una API resulta útil conceptualizarla como un módulo visto desde fuera. El módulo reúne una capacidad y conserva su implementación; la API es el límite por el que otros colaboran con él.
No son exactamente lo mismo. Un módulo puede ofrecer más de una conversación a consumidores diferentes y también consumir otras APIs. La equivalencia sirve como herramienta de diseño: cuando aparece una API relevante, buscamos el módulo responsable de sostener su contrato.
módulo
├── API ofrecida → consumidores
├── implementación oculta
└── APIs consumidas → otros módulosEl módulo puede materializarse como una función, un tipo, un paquete, un proceso o varios elementos coordinados. Su forma técnica no determina su escala conceptual.
El interés está en las interacciones#
API-DD pone el interés en lo que ocurre entre módulos: mensajes, respuestas, errores, efectos y garantías. Esta mirada sigue la idea de Alan Kay presentada en la introducción: los sistemas crecen mejor cuando se diseña cómo se comunican sus módulos, no cuando se fija de antemano todo su interior.
Mirar las interacciones aclara qué necesita el consumidor, qué módulo responde, qué puede observarse y qué decisiones internas pueden cambiar. También muestra las APIs que ese módulo consume.
El algoritmo sigue siendo importante, pero pertenece a otro nivel. Primero se distingue qué debe sobrevivir a cualquier implementación correcta y después se elige cómo conseguirlo.
Todos los fundamentos se repiten#
Cuando un módulo se descompone en módulos con contratos propios, los cinco fundamentos pueden aplicarse de nuevo en cada límite:
| Fundamento | Pregunta que reaparece | |---|---| | Recursión | ¿Qué módulos y conversaciones existen en esta escala? | | Vocabulario | ¿Qué significan sus nombres y mensajes? | | Visibilidad | ¿Qué necesita conocer cada consumidor? | | Autonomía | ¿Puede el módulo cumplir su contrato y entregar resultados sin estado mutable compartido? | | Testeabilidad | ¿Puede verificarse mediante observaciones públicas? |
La recursión no convierte cada parámetro, helper o estructura en otra API. Value, Result o Error forman parte del vocabulario de un mensaje. Solo pasan a considerarse módulos cuando reúnen comportamiento, tienen consumidores o necesitan evolucionar mediante un contrato propio.
Dónde continuar y dónde detenerse#
La perspectiva se repite cuando una capacidad tiene otro consumidor, garantías propias o evolución independiente, o cuando la interacción cruza un límite técnico u organizativo relevante.
Se detiene cuando la decisión solo explica cómo trabaja el módulo actual. Un bucle, un índice o una función auxiliar no necesitan una API propia si ninguna relación externa depende de ellos.
Esto evita confundir recursión con una jerarquía infinita de interfaces. El objetivo es reconocer límites útiles, no fabricar capas.
Ejemplo en Go#
Un Pipeline ofrece una API y consume la API de cada Stage. El recorrido del arreglo permanece dentro de su implementación.
type Stage interface {
Apply([]byte) ([]byte, error)
}
type Pipeline struct {
stages []Stage
}
func (p Pipeline) Run(value []byte) ([]byte, error) {
var err error
for _, stage := range p.stages {
value, err = stage.Apply(value)
if err != nil {
return nil, err
}
}
return value, nil
}Pueden existir muchas implementaciones de Stage. Cada una puede revisarse otra vez como módulo, mientras que el recorrido del slice sigue siendo un detalle de Pipeline.
Revisión práctica#
Nombra la API, el módulo responsable, sus consumidores y las APIs que necesita. Solo profundiza en colaboraciones con contrato propio; algoritmos y helpers permanecen dentro del módulo. La descomposición es útil si aclara una relación real.
La recursión sigue las conversaciones entre módulos, no cada línea de código.