Por favor, resuelva una discusión entre un amigo y yo.
Actualmente estamos diseñando una API de producto. Nuestra entidad de producto se ve así
{
"Id": "",
"ProductName": "",
"StockQuantity": 0
}
Las ventas de productos son manejadas por un tercero y están obligadas a informarnos con la cantidad comprada para que el StockQuantity
campo se pueda disminuir.
Mi acercamiento:
PUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" }
El tercero es responsable de consultar el producto, hacer el cálculo en función de la StockQuantity
cantidad actual y comprada y enviar una PUT
solicitud con el nuevo valor.
Mi amigo no quiere que el tercero haga el cálculo. Su acercamiento
PUT /api/Product/{Id}/DecreaseStock --data { "PurchasedQuantity": "{PurchasedQuantity}" }
Entonces podemos hacer el cálculo y actualizar el StockQuantity
No quiero crear puntos finales basados en funciones y él no quiere confiar en terceros para hacer los cálculos.
¿Cuál sería la forma correcta de abordar este problema?
fuente
Respuestas:
Puede dejar que su tercero publique las ventas de su producto. Por ejemplo:
Estoy de acuerdo con tu punto y el de tu colega. Esta es la lógica de negocios, y no debe dejarse al cliente de la API, pero también debe evitar tener "funciones" como puntos finales.
A veces, resolver estos problemas es tan fácil como llamarlo de manera diferente, es cierto que no siempre.
fuente
/sale
punto final sigue siendo válida?/sale
y/product/{id}/sale
son completamente independientes y el hecho de que tengan nombres similares no implica de ninguna manera que se refieran al mismo recurso.sale
que no está en mi dominio y no forma parte de élproduct
. ¿Todavía tiene sentido crear/product/{id}/sale
mientras no representa ningún recurso real?No hay razón para que tú tampoco puedas hacerlo; o ambos.
En un contexto de punto de venta, el seguimiento de transacciones individuales tiene mucho sentido. Allí, la solución de Robert tiene mucho sentido.
En un contexto de stock / almacén, no necesariamente realiza un seguimiento de las transacciones sino de "hacer un inventario"; Tener un punto final que permita al cliente informar sus niveles de existencias
Tiene mucho sentido.
Los niveles de existencias cambian por otras razones que no sean "ventas"; Sólo algo para tener en cuenta.
En teoría, el nivel de existencias debería ser computable a partir de los cambios; pero en algunos dominios esa es precisamente la suposición que desea verificar . Desearía poder calcular el nivel de existencias de dos maneras diferentes y verificar las discrepancias (también conocido como "contracción").
Por lo tanto, no creo que la semántica sea clara, según el contexto que haya proporcionado.
En cuanto a la parte HTTP;
PUT [target-uri]
tiene sentido semánticamente cuando reemplaza una representación de un documento con otra. Es unUPSERT
- el segundo PUT a un recurso está pidiendo sobrescribir la representación existente.dice que la cantidad de unidades vendidas es
3
, no10
.Eso es lo que
10
pareceEsa es otra forma de deletrear
10
.En lo que respecta a HTTP, esto también es aceptable. Sin embargo, no es una gran opción en una red poco confiable porque los mensajes a veces se duplican.
Es eso
13
? o10
?Eso es inequívocamente
10
Eso es inequívocamente
10
Eso es sin ambigüedad
13
Eso es sin ambigüedad
13
10
13
(Para ser justos, HTTP tiene soporte para solicitudes condicionales ; puede levantar algunos de los metadatos de su protocolo específico de dominio a los encabezados agnósticos de dominio para eliminar parte de la ambigüedad, si puede persuadir al cliente para que siga adelante).
Por supuesto, hay compensaciones: HTML no tiene soporte PUT nativo; si tiene la intención de que los clientes de su API sean navegadores, entonces necesita un protocolo basado en POST o necesita extensiones de código a pedido para convertir el envío del formulario de POST a PUT.
fuente
Esto parece un diseño realmente malo, no importa cómo lo cortes. Nunca confiaría en que un tercero me diga mi inventario actual a menos que los haya contratado para administrar mi almacén.
Además, el enfoque de búsqueda de funciones no es RESTANTE en absoluto y está destinado a crear consternación entre sus consumidores.
Finalmente, no puedo imaginar un escenario en el que lo único que le importa de una venta es el inventario resultante que le queda después de que se haya realizado.
Es mucho mejor que el tercero le publique un recurso de Venta o Factura (con información como qué producto, cantidad, fecha, método de envío, información del cliente, etc.). Esto le permite hacer un análisis real y realizar un seguimiento de lo que está vendiendo, a quién, cuándo, etc. para que pueda actualizar su negocio.
Incluso si su tercero está haciendo el cumplimiento total del pedido, deseará realizar un seguimiento de las ventas para fines contables y demográficos de los clientes, si nada más.
fuente
Este tipo de diseño tiene un problema importante en el sentido de que si alguna vez desea que se ejecute más de un hilo de cliente en su API, está sujeto a lecturas / escrituras sucias. Es decir, entre el momento en que el cliente extrae la cantidad actual y calcula el nuevo valor, otro cliente puede extraer ese mismo valor anterior y calcular una respuesta diferente. La cantidad con la que termines será la que se actualice por última vez, pero ninguna de las dos es correcta. Por ejemplo, supongamos que su cantidad actual es 10. El cliente A quiere vender 5 artículos y extrae la cantidad actual. Al mismo tiempo, el cliente B quiere vender 6 artículos y extrae la cantidad actual. Ambos ven 10 artículos en stock. A calcula 5 elementos restantes. sicalcula 4 restantes. Ambos se actualizan. Ahora muestra 4 o 5 elementos restantes dependiendo de quién fue la última actualización registrada. Sin embargo, en realidad ha vendido más artículos que realmente tiene. Lo peor es que no hay una manera fácil de caminar y ver qué salió mal. Todo lo que tienes son dos incorrectos
PUTs
en tus registros para mirar.En cualquier sistema de registro del mundo real, simplemente tener un total actual no es adecuado. Considere si va a una tienda y compra una cantidad de artículos. Solicita un recibo y el cajero solo le entrega un recibo con un total total. ¿Cómo mostrarías que el total es correcto de ese recibo? ¿Cómo mostrarías que compraste un artículo si quisieras devolver algo?
El enfoque de su amigo es mejor, pero sugeriría agregar una identificación de transacción en la mezcla. Esto aborda las preocupaciones reales que VoiceOfUnreason menciona sobre transacciones duplicadas. Una opción es proporcionar una
POST
operación para crear una nueva transacción y luegoPUT
a esa transacción para confirmarla. En el momento de la confirmación, reduce el stock total o rechaza la solicitud porque no hay suficiente disponible.fuente
Dado que las ventas son manejadas por terceros, debe tener control sobre su inventario de productos al no permitirles actualizar el recuento de existencias.
Para uso interno, por ejemplo, con fines de recuento de existencias, puede tener su enfoque, es decir
PUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" }
.Para uso externo, debe crear una interfaz separada, por ejemplo,
/api/SalesOrder/
que tome una lista de productos y cantidades, como:Según lo
SalesOrder
enviado por terceros, la cantidad de cada producto puede actualizarse y asignarse al pedido o puede rechazar el pedido si no hay suficiente producto disponible.El procesamiento y el recuento de existencias es un proceso interno, los terceros solo requieren una interfaz para que puedan enviar sus pedidos al inventario. Básicamente, así
SalesOrder
es como se comunican las ventas, las finanzas y el almacén para completar una venta.fuente