CodeGym /Cursos /Spring REST & MVC /API RPC-like vs REST: sin comandos

API RPC-like vs REST: sin comandos

Spring REST & MVC
Nivel 3, Lección 3
Disponible

1. Cómo la API deriva hacia el estilo RPC-like

Ya tenemos la gramática básica: colección, recurso individual, GET/POST/PUT/PATCH/DELETE. Pero el negocio casi nunca llega con la formulación «añadid otro PATCH». Dice: completar la tarea, asignar un responsable, adjuntar un archivo, eliminar un comentario. Y es precisamente en ese momento cuando la API más frecuentemente vuelve a derivar en un catálogo de comandos.

A nivel del servicio esto es normal: dentro de la aplicación completeTask() o uploadAttachment() suenan de forma natural. El problema empieza cuando esos mismos verbos se convierten en rutas públicas como /completeTask y /uploadFileToTask. El contrato externo de nuevo describe botones, no objetos del dominio.

Aquí nos interesa la pregunta práctica: cómo traducir el idioma de las acciones del negocio a la ya conocida gramática de recursos.

API RPC-like: sin religión ni «guerras santas»

Por API RPC-like aquí entendemos no una tecnología concreta, sino un patrón característico del contrato: la ruta responde a la pregunta «¿qué ejecutar?», y no «¿con qué trabajamos?». Externamente la diferencia normalmente se ve así:

Eje de comparación Enfoque RPC-like (comandos) Enfoque orientado a recursos
Sentido principal del URI «¿Qué hacer?» «¿Con qué trabajamos?»
Cómo crece la API Nueva funcionalidad → nuevo endpoint de acción Nueva funcionalidad → refinamiento del recurso/representación/subrecurso
Cómo el cliente «sostiene el modelo» Hay que memorizar el catálogo de comandos Se puede deducir por la estructura de los recursos
Método HTTP más frecuente Normalmente todo se convierte en POST Los métodos se usan por su sentido (GET, POST, PATCH, DELETE...)

Nadie prohíbe los comandos dentro de la aplicación. La pregunta es solo si el cliente debe aprender tus nombres de métodos en lugar de un mapa comprensible de recursos.

2. Cuando la API crece en un catálogo de comandos

El estilo de comandos casi siempre crece de la misma manera: para cada nueva petición del negocio aparece otra ruta con un verbo. En Task Tracker API esto rápidamente se convierte en algo como:

import java.util.List;

// Así se ve un «catálogo de comandos»: el crecimiento va añadiendo nuevos verbos en las rutas
List<String> endpoints = List.of(
    "/createTask",
    "/completeTask",
    "/assignTask",
    "/addCommentToTask",
    "/uploadFileToTask"
);

El problema no es solo la cantidad de rutas. El cliente tiene que aprender tu catálogo de comandos, la documentación se fragmenta en una lista de acciones inconexas, y la API deja de parecer un mapa del dominio. En cuanto notas esto, el siguiente paso casi se sugiere solo: tomar cada acción del negocio y preguntar qué objeto aparece, cambia o se lee en ese proceso.

3. Traducción de acciones al mundo de los recursos

Ahora lo más útil: qué hacer cuando el negocio dice «necesitamos la acción X», y nosotros queremos mantenernos en el estilo de recursos. La respuesta pragmática buena casi siempre empieza no por la elección del URI, sino por una simple pregunta: ¿qué objeto del dominio aparece o cambia en ese proceso? Si respondiste honestamente a eso, el diseño normalmente «se cierra» solo.

Aquí hay una pequeña chuleta en forma de árbol de decisiones. No es sobre la teoría perfecta, sino sobre que el cerebro no se apague:

flowchart TD
    %% Idea: empezamos no por el URI, sino por la pregunta «qué objeto aparece/cambia?»
    A["Necesitamos una acción (business operation)"] --> B{"¿Aparece un nuevo objeto?"}
    B -- Sí --> C["Es creación de recurso → colección (por ejemplo, comments/attachments)"]
    B -- No --> D{"¿Cambia el estado de un objeto existente?"}
    D -- Sí --> E["Es update del recurso → cambiamos la representación (por ejemplo, estado/responsable)"]
    D -- No --> F{"¿Solo hay que obtener datos?"}
    F -- Sí --> G["Es read del recurso/colección"]
    F -- No --> H["Comprueba: ¿estamos intentando ocultar un proceso complejo detrás de un 'botón'?"]

Veamos directamente en nuestro dominio. Así es como un «comando» se convierte en una solución de recursos sin magia:

Imaginemos el comando: «Añadir un comentario a la tarea». Si se piensa en comandos, la mano tiende a hacer /addCommentToTask. Pero si preguntas «¿qué aparece?», la respuesta es simple: aparece un nuevo comentario. Por lo tanto, el comentario es un recurso (subrecurso de la tarea), y tiene su lugar en la API como colección de comentarios dentro de la tarea.

import java.util.Map;

// Traducción mental: «nombre de la operación» -> «dónde encaja en el mapa de recursos»
Map<String, String> operationToResource = Map.of(
    // El comentario es un nuevo objeto, por lo tanto es la creación de un elemento en la colección comments
    "addCommentToTask", "/tasks/{taskId}/comments",
    // El adjunto también es un nuevo objeto (metadatos + archivo), por lo tanto es la colección attachments
    "uploadFileToTask", "/tasks/{taskId}/attachments"
);

Comando: «Cargar un archivo a la tarea». Aparece un nuevo objeto — los metadatos del adjunto. Por lo tanto, también es la creación de un subrecurso: la colección de adjuntos en el contexto de la tarea.

Comando: «Asignar un responsable». Aquí no aparece ningún objeto nuevo, pero cambia el estado de la tarea (por ejemplo, el campo assigneeName). Por lo tanto, es una modificación del recurso existente task. En el modelo de recursos el cliente trabaja con la tarea, y la asignación es simplemente un cambio de su estado, no un «comando del mundo separado».

Comando: «Completar la tarea» (complete). Si somos honestos, en nuestro dominio probablemente sea la transición del estado a DONE. De nuevo: cambia el estado de la tarea, no aparece una entidad separada «Completion». Por lo tanto, la tarea sigue siendo el recurso principal, y «complete» es un caso particular del cambio de su estado.

Disciplina muy importante: dentro del servicio puedes seguir usando métodos verbales. Es normal. Simplemente la API externa no debe copiar sus nombres.

class TaskService {
    void completeTask(String taskId) {
        // Dentro del servicio el verbo es natural: es parte de la implementación de la operación de negocio
        // Por fuera (en el contrato HTTP) no queremos convertir esto en una dirección tipo /completeTask
    }

    void assignTask(String taskId, String assigneeName) {
        // Igual: el comando interno está bien
        // Por fuera normalmente será un update del recurso /tasks/{taskId} (por ejemplo, el campo assignee)
    }
}

El problema no está en los verbos como tales. El problema está en que el verbo se convierte en una dirección en el contrato público, y el cliente empieza a vivir en tu «vocabulario de comandos», y no en el dominio.

4. REST pragmático: disciplina sin perfeccionismo

El REST pragmático aquí no sirve para discutir la pureza del estilo, sino para no caer de inmediato en dos extremos: en el catálogo de comandos por un lado y en el perfeccionismo académico por el otro. Si el modelo de recursos hace la API más comprensible para el cliente, vale la pena mantenerlo; si la discusión sobre el «REST perfecto» no añade nada al contrato, solo hace ruido.

Es útil comparar los tres estados en los que los equipos suelen encontrarse:

Estilo Cómo se ve desde fuera Qué ocurre dentro del proyecto
Caos RPC-like Muchas rutas verbales, «action endpoint» para cada detalle El cliente aprende comandos, la API crece como lista de botones
REST dogmático Todo estrictamente «según el libro», pero a veces excesivamente complejo e incómodo A veces aparecen soluciones extrañas «por la corrección»
REST pragmático El recurso en el centro, las acciones se expresan a través del estado/subrecurso, no a través de catálogos de comandos La API permanece legible, pero no interfiere con el desarrollo real

Y aquí hay un punto importante: a veces en el desarrollo comercial real se encuentran operaciones que es difícil expresar sin una «acción». Por ejemplo, «exportar un informe» o «lanzar un recálculo» puede ser un proceso separado que vive como un recurso «job». Pero observa: incluso allí un buen diseño a menudo convierte la acción en un recurso («tarea de exportación», «informe de exportación»), y no en /doExportNow.

En Task Tracker API mantenemos conscientemente el modelo simple: la historia principal es tasks, y de auxiliar — comments y attachments; tags — lista de valores de lookup. En esa escala REST pragmático casi siempre significa una cosa: no proliferar comandos si el sentido se puede expresar a través de un recurso, su estado o un subrecurso.

5. Test rápido: señales de una API de comandos

Cuando diseñas una API, es útil tener una herramienta rápida de autocomprobación — como el olor a humo en la cocina. No hace falta entender de inmediato qué exactamente se está quemando, pero es importante notar a tiempo que «algo claramente va mal». Para el diseño RPC-like tal test se puede reducir literalmente a tres preguntas. Y no hay que convertirlo en burocracia: es simplemente una breve pausa antes de añadir un nuevo endpoint.

La primera pregunta suena así: «¿Puedo decir en una frase qué recurso estoy dirigiendo?» Si dices «estoy llamando a completeTask», el recurso no apareció. Si dices «estoy trabajando con una tarea concreta», el recurso apareció.

La segunda pregunta: «¿Aparece un nuevo objeto?» Si sí, la mayoría de las veces es la colección — creación de un nuevo elemento — y casi automáticamente llegas a un subrecurso o una colección separada. Un comentario es un ejemplo perfecto: aparece, tiene su propio id, y vive en el contexto de la tarea.

La tercera pregunta: «¿No estoy añadiendo un nuevo botón en lugar de describir un cambio de estado?» Si haces /changeStatus y /assignTask, se parece mucho a «botones». Si en cambio dices «la tarea tiene estado y responsable, y cambiamos el estado de la tarea», el modelo se vuelve más natural.

Incluso se puede hacer una verificación de juguete en el código — no por el «algoritmo correcto», sino simplemente para que el cerebro se detenga un momento:

record ApiCheck(String path) {
    boolean looksLikeCommand() {
        // Esto no es un «validador de REST», sino una heurística gruesa: un verbo al inicio de la ruta = bandera roja
        return path.startsWith("/create")
            || path.startsWith("/update")
            || path.startsWith("/delete")
            || path.startsWith("/complete")
            || path.startsWith("/upload");
    }
}

// Ejemplo: la ruta empieza con un comando, así que la heurística actúa
ApiCheck a = new ApiCheck("/completeTask");
System.out.println(a.looksLikeCommand()); // true

Una vez más: esto no es un «detector de REST». Es un recordatorio: si ves que la ruta empieza con un verbo-comando, detente e intenta reformular la pregunta: «¿qué recurso hay aquí realmente?»

6. Errores típicos al intentar «evitar el RPC-like»

Error n.° 1: confundir «RPC-like malo» con «los verbos están prohibidos en absoluto».
Los principiantes a veces toman la regla «no hagas /completeTask» y la convierten en un dogma de «en el URI nunca puede haber verbos». El resultado es un extraño juego de eufemismos: la ruta no se vuelve más clara, simplemente se hace menos honesta. En esta lección lo importante no es la prohibición de la propia palabra, sino la pregunta de legibilidad: ¿se ve el recurso y su lugar en el modelo?

Error n.° 2: reemplazar un endpoint de acción por otro, sin volver al recurso.
A veces el equipo entiende que /uploadFileToTask queda mal, y hace, pongamos, /taskFileUpload. Es cosmética. Si no respondiste a la pregunta «¿es esto un subrecurso? ¿un nuevo objeto? ¿un cambio de estado?», simplemente cambiaste el letrero en la misma tienda de comandos.

Error n.° 3: intentar «hacer REST» a través de un cambio de nombre mecánico, pero dejar el modelo de comandos.
A veces la ruta ya parece «orientada a recursos», pero dentro sigue viviendo un comando: siempre POST, siempre formas diferentes de datos de entrada, cada endpoint vive su propia vida. REST no es solo «cómo nombrar la ruta», también es un modelo estable de operaciones alrededor de los recursos. Por ahora no nos adentramos en los detalles de implementación, pero hay que recordar: el cambio de vocabulario sin cambio de mentalidad no cura nada.

Error n.° 4: percibir el pragmatismo como permiso para el caos.
«REST pragmático» no es «hagamos lo que sea más rápido». Es «mantengamos el modelo de recursos y no lo compliquemos donde no aporte valor». Si bajo la bandera del pragmatismo empiezas a añadir /doThis, /doThat, /doSomethingElse, simplemente vuelves al estilo RPC-like, solo que ahora con una indulgencia moral encima.

Error n.° 5: olvidar que los comandos dentro de la aplicación son normales, pero hacia fuera no.
A veces el desarrollador empieza a luchar con su propio código: «Ya que es REST, entonces dentro tampoco se puede escribir completeTask()». No hace falta eso. Dentro construyes la implementación, y allí los métodos verbales son algo natural. Hacia fuera construyes un contrato, y allí importa más el mapa de recursos y la predictibilidad. Cuando tienes ante los ojos un mapa de recursos compacto del proyecto, ese límite se vuelve mucho más visible: dentro pueden vivir comandos del servicio, pero hacia fuera no sale un catálogo de botones, sino el mapa del dominio.

1
Tarea
Spring REST & MVC, nivel 3, lección 3
Bloqueada
Detector de rutas de comando
Detector de rutas de comando
1
Tarea
Spring REST & MVC, nivel 3, lección 3
Bloqueada
Traducción de acciones de negocio al lenguaje de recursos
Traducción de acciones de negocio al lenguaje de recursos
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION