Broadleaf Microservices
  • v1.0.0-latest-prod

Cart Versioning

Carts are versioned (optimistically locked) entities, meaning that they have a numeric version that is incremented each time an update is made to them. On most requests to Cart Operations that modify the cart, the cart version must be supplied if the cart already exists (i.e. is being updated). Modifying requests that supply an out-of-date (lower) cart version will be rejected with an HTTP 409 (Conflict). Clients of the API are expected to keep track of the cart version each time they receive a cart response from the API, which will also include the latest cart version.

In short, every cart-modifying request should supply the known cart version, and expect to receive and store an updated cart version once the request completes.

Supplying the Cart Version

The cart version may be supplied on the request in either:

  • A request header: X-Cart-Version (recommended)

  • The body: Typically under $.version when performing an operation that targets the cart (e.g. adding a promo code or a new item), or $.cartVersion when performing an operation that targets a specific cart item (e.g. updating item quantity).

Receiving the Cart Version

As almost all cart-modifying endpoints return the full cart, the updated cart version can be retrieved from the body in $.version (where $ is the Cart object). However, it is recommended to store the state of the cart as a whole and use the X-Cart-Version header with the current cart’s version for consistency.

Handling Conflicts

Avoidance

Most conflicts (HTTP 409s) can be avoided by following these recommendations:

  • Always update the locally-stored cart version from the cart response bodies received

  • Prevent duplicate submissions from the user (e.g. by disabling the button that triggered the submission while the request is in process)

  • Provide a way to keep cart state across different tabs in the browser

Conflict Triggers

In regard to how the version is managed by the backend, it’s important to remember that Cart Services is where the cart is persisted and the version is incremented by the JPA implementation, but the the version is checked in multiple places, including Cart Operations.

On every request, Cart Operations attempts to resolve the current cart for the user. If Cart Operations is able to resolve the cart, it performs its own check to make sure the request version is current, and aborts the request with a 409 if not (also responding with the resolved cart so that the frontend can update and retry if desired). If the versions do match, the request proceeds normally, eventually resulting in a modifying request to Cart Services.

That request to will trigger one or more database writes to the cart, which will fail if the cart being updated to is older (lower version) than the same cart’s state in the database. It’s important to note that this is the "catch-all" for validating the version, and that the prior check in Cart Operations will be effective most of the time, but with race conditions will not always prevent stale data from being written to the cart. The check in Cart Operations is there to avoid unnecessary processing of requests with stale cart data that will just be rejected in Cart Services anyway.

Resolution

In some cases, the conflicts will be unavoidable. Broadleaf’s out-of-box behavior will be to reject updates with an incorrect version, and delegates to the frontend for how to handle that situation. When receiving an HTTP 409 due to a cart version conflict, the response body will contain the most recent version of the cart in $.newCart. It is ultimately a business decision as to what should be done. Some options include:

  • Update the cart from the response body, and inform the user that they can try again. While this is probably the safest option, it may be cumbersome to the user.

  • Update the cart from the response body, and automatically reapply the operation the user attempted. User experience is better, but this may not be ideal in certain scenarios, such as if the user tried to change quantity on an item that was just removed from the cart.

  • Update only the version on the failed request and retry. This should likely only be done if the operation is idempotent.