0

I'm looking at a scenario where we have one parent resource (real name removed to protect the innocent). As a child object, we have a list of prices organized by effectiveDate.

Preexisting CRUD on api/resource
New POST on api/resource/{code}/someRelationship/{relId}/prices

A few months ago we created the POST prices endpoint. It takes a list of price objects. Currently this appends new prices to a pre-existing list in the database. There's no handling of duplicates when we consume these prices other than the last in wins if the effectiveDates are the same. We did not expose an id and just wanted to associate some prices to the parent resource object given someRelationship.

{
  "prices": [
    {
      "price": 0,
      "effectiveDateTime": "2020-02-14T18:30:33.710Z"
    }
  ]
}

Life went on. Things got more complicated. We ran into a situation where something might change in the source of truth system where the effectiveDate shifts. If this happens, we believe that we will incorrectly leave behind remnant prices on the system that we're synchronizing to. The new ones will get pushed over, but there will be old prices that are no longer correct. It seems drastically easier to just take whatever the system we're synchronizing from gives us because it's the source of truth for prices.

Options:

  1. Lean into POST api/resource/{code}/someRelationship/{relId}/prices
    • Can we continue to treat the prices object as a list belonging to the parent resource rather than a first class, restful object?
    • Change the POST logic to wipe out the old prices, and save the new ones that were broadcast by the calling system since it knows about ALL the prices and can save them in one call.
    • We can argue if it should be named PUT or POST. Sounds like POST to me.
    • I'm told this breaks all that is holy with restful conventions.
    • Would have to update api docs to communicate this functionality because we broke the restful conventions.
  2. promote the prices object to a first class, restful object of the api.
    • But this means we'll have to create full CRUD endpoints exposing the id
      • or use the effectiveDateTime as the id which seems weird
    • Workflow for the caller to juggle full CRUD of prices
      • GET /api/resource/{code}/someRelationship/{relId}/prices
        • to get a list to know what changes to push over
      • price in both sides with the same value and effectiveDate? Do nothing.
      • POST /api/resource/{code}/someRelationship/{relId}/prices
        • price missing from side to be synchronized? POST it one by one? Or can we add it to a list and POST the list? what if we have 100 prices? 100 APIs calls?
      • PUT /api/resource/{code}/someRelationship/{relId}/prices/{id}
        • price wrong from side to be synchronized? PUT it one by one.
      • DELETE /api/resource/{code}/someRelationship/{relId}/prices/{id}
        • price in the side to be synchronized but missing in the source of truth? Delete it one by one.
    • comparison logic could get more confusing if we continue to add fields to what a price is (more than price and effectiveDate)
    • Example: Multiply by 12 months times a few parent resources. 12 months * 100 price API calls * 5 resources = 6000 calls?
agibson
  • 23
  • 5
  • How about just adding a new PUT endpoint that replaces an old list with a new one? Then the existing POST endpoint and the `prices` resource don't have to change. – jaco0646 Feb 14 '20 at 22:08
  • I thought you needed an id when PUTing. In this case we would not have one. Are you suggesting to call `PUT /api/resource/{code}/someRelationship/{relId}/prices` without an id just taking the list of prices? EDIT: This is interesting... https://stackoverflow.com/a/43393302/202963 – agibson Feb 14 '20 at 22:21
  • Resources in ReST are identified by URIs. If you have a (unique) URI to point to, then you have an ID. – jaco0646 Feb 14 '20 at 22:25

0 Answers0