0

I'm aware of this Q/A. The answer doesn't help my situation much.

I'm designing a CQRS-based RESTful service that provides the current reputation of existing accounts. The reputation calculation can take up to 30 seconds.

Due to the long calculation time, and due to our desire to use CQRS on this for other reasons, the entire process is done in two (or more) requests:

FIRST REQUEST

  • A client tells the RESTful service to begin the calculation for the account.
  • The RESTful service basically responds "OK" and has another service start the calculation.

SUBSEQUENT REQUESTS

  • The client asks the RESTful service for the calculated reputation score, if available.
  • The RESTful service responds with either the reputation score, or a note that it's still calculating.

QUESTION #1

How should I structure the first request URI? For account number 12345, should it be something like one of the following?

PUT /accounts/12345   payload: {}

I'm concerned about this approach because I read PUT should be idempotent.

Another option:

POST /accounts/12345   payload: {}   // ...but shouldn't POST contain the whole entity in the payload?

...or, maybe change the entity from an account to a command...

POST /command/reputation-calculation/12345  payload: {}  // ...feels like we're getting off-course here

...or something else?


QUESTION #2

For the second request, that seems a bit more straightforward. Should the URI be something like this?

GET /accounts/12345/reputations

I appreciate your suggestions. Thanks.

Jonathan M
  • 17,145
  • 9
  • 58
  • 91

3 Answers3

1

I may have found an answer. It involves moving the CQRS away from the client, and into the control of the RESTful service, which may employ it optionally.

For each client request, the URI could be:

GET /accounts/12345/reputations

Upon receipt, the RESTful service could check to see if a recently-calculated reputation is available. If a recent reputation is available, the RESTful service replies with a 200 OK status and delivers the response payload containing the reputation.

If no recent reputation is available (nor is it in-process according to the calculating service), the RESTful service kicks into CQRS mode. It tells the calculating service to begin calculating the reputation.

Then, whether it initiated the calculation, or whether it found one already in process, it returns to the client a 202 Accepted with follow-up link.

This kind of async situation seems to be what was intended with the 202 Accepted response, per the docs.

Jonathan M
  • 17,145
  • 9
  • 58
  • 91
1

REST is not a great fit for CQRS command-oriented systems. So if the client is command-oriented then just use HTTP as the transport.

Alternatively, the client could create a reputation-enquiry resource that has a status of calculating or ready. The back-end decides if the result of a recent calculation can be reused, in which case the status will be immediately ready. A score is supplied when the resource is ready.

Peter L
  • 2,921
  • 1
  • 29
  • 31
  • Indeed, HTTP is the transport, as noted in the accepted answer. I've given a +1 on your idea of encapsulating the client's CQRS capabilities in a `reputation-enquiry` resource. Much thanks. – Jonathan M Jul 23 '19 at 14:29
0

How should I structure the first request URI?

The spelling of the URI doesn't matter -- the machines don't care very much. If you think about how generic clients cache documents, you might choose to use a target-uri specifically to invalidate one specific resource in the cache.

Using POST is a natural fit with the way HTTP caching works. Also, it's how we would do it in HTML, so you know it has a track record.

So if you do GET /accounts/12345 to view the current reputation score, then POST /accounts/12345 is a perfectly reasonable way to kick off the upgrade process.

If you were using hypermedia (which is one of the REST constraints) then the response you get from the POST would include in it the URI for the status monitor. So that URI could be anything you want (since the client just goes where you tell it to). The server will probably want to get a hint back to know which update you are asking about; so it could be GET /reputationUpdate/67890.

There might be advantages to having the status monitor in the same hierarchy as the account, so GET /accounts/12345/reputationUpdate/67890 would also be fine.

Spellings that are consistent with the web linking specification often make implementing the client or server easier, because you can grab a library off the shelf that understands templates.

Community
  • 1
  • 1
VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • Thanks. My main concern about `POST /accounts/12345` is (1) there is no payload because I'm basically disguising a query as a command, and (2) the URI doesn't show my intent. In fact, it looks like I'm updating the whole account. If I'm going to use POST, perhaps something like `POST /accounts/12345/reputation` is closer to showing the intent that I'm issuing a command to kick off the calculation process. In the end, I think my answer provided here may be the better direction, because in truth, the client is making a query. It shouldn't have to deal with CQRS directly in this case. – Jonathan M Jul 12 '19 at 20:32