18

Consider the following coarse grained REST apis for a Contact resource

POST          /api/contacts                             
GET           /api/contacts                             
GET           /api/contacts/:id                         
PUT           /api/contacts/:id                         
DELETE        /api/contacts/:id                         

Consider using eventsourcing for the contact resource, i.e. commands are validated and events are stored. So every event must be stored, including each field level change.

CreateContactCommand -> | Contact("john", "doe", 25) | -> ContactCreatedEvent
FirstNameChangeCommand -> | Contact("jane", "doe", 25) | -> FirstNameChangedEvent
LastNameChangeCommand -> | Contact("jane", "dear", 25) | -> LastNameChangedEvent
AgeChangeCommand -> | Contact("jane", "doe", 30) | -> AgeChangedEvent

Now, combining REST and EventSourcing both.

Doing REST, how the client communicates to the above standard REST APIs for field level changes to generate commands at server side REST end point?

Major question is, how to design REST API so that it can also support the commands eventually supporting eventsourcing?

If anybody could shed light on this, the help would be greatly appreciated.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
PainPoints
  • 461
  • 8
  • 20

2 Answers2

11

CQRS and event Sourcing is neither an API design principle, nor is it a top-level architecture. Still, if you want to 'expose' your API as a task-based API, you can expose links as part of the contact resource.

GET /contacts/1234

Response

200 OK
<contact>
  <atom:link href="/contacts/1234/first-name" rel="first-name" />
  <atom:link href="/contacts/1234/last-name" rel="last-name" />
  <atom:link href="/contacts/1234/age" rel="age" />
  <first-name>Jane</first-name>
  <last-name>Doe</last-name>
  <age>25</age>
</contact>

The assumption here is that you change the API to a true level 3 REST API.

Also, /contacts/1234 will only accept GET and DELETE (not PUT) requests. If a client wants to change e.g. the first name of a contact, it must follow a link with the relationship type first-name and make a PUT request against that resource:

PUT /contacts/1234/first-name
<first-name>John</first-name>

Anything else than a first-name field PUT here should be ignored or rejected.

Thus, when the service receives a PUT against a first-name resource, it's a command to change the first name of the contact.

This still isn't a proper task-based API because it doesn't capture why the first name changes, but I hope you get the idea.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Thanks Mark for clicking the idea. It will help me get started. – PainPoints Sep 09 '15 at 13:04
  • 1
    +1 to "This isn't a proper task-based UI". Chances are your commands and events are much too granular for your system's own sanity. – guillaume31 Sep 10 '15 at 09:48
  • 1
    So if you need to change the first and last name you have to produce 2 requests? Also, you have to handle each of this request separately. Writing generic handler will lead you to the implementation of kind of PATCH method... Looks at least strange. – S2201 May 09 '17 at 11:07
  • 1
    @Savash This post is mostly about DDD, CQRS, and *task-based APIs*. As I remarked in my answer, the example isn't really *task-based*, but I was attempted to get a point across in a digestible manner. It's not really about changing the first name or the last name, but about providing information about *why* things change. If you have a reason to change both first name and last name, you could provide a semantic link for that as well, but keep in mind that this isn't CRUD. – Mark Seemann May 09 '17 at 15:22
  • @MarkSeemann Don't you think that the properties that should be changed together are usually grouped into the models and creating the semantic links for that lead us to the same REST? – S2201 May 22 '17 at 09:23
  • @Savash That's one of Kent Beck's rules of thumb: _things that change together should go together; things that change at different rates should not_... (paraphrasing from memory). – Mark Seemann May 22 '17 at 12:21
0

REST is a distributed applicative architectural style for the Web. The Web has its own rules and constraints, it's a realm of representations and resources, not tasks, commands or queries.

The Web is lower level than your application. When designing your HTTP resources, you have to think about caching, versioning, repeatability, performance, loose coupling in a Web context, so you might not want to make them match domain entities exactly, let alone properties of these entities. In addition, HTTP has a very limited number of verbs, and when verbs fail you, resources are not necessarily the best choice to describe a task or an action.

Therefore, a strict one-to-one correspondence between the commands that make sense in your domain and a (resource, verb) couple isn't always desirable or adequate. You may want to go further and design your own Domain Application Protocol for REST clients to talk to the server according to finer rules. A DAP is reflected in hypermedia links and relationships for transitions, as Mark pointed out, but you can also use custom content-types to better describe the type of payload in requests or responses. They could, for instance, contain the type of domain command you're sending.

If you look at this article, you'll see that the (POST, api/inventoryItem/{id}) couple isn't unique in the chart under "Resources". It can be used to transport either a RemoveItemsFromInventoryCommand or a CheckInItemsToInventoryCommand. How you specify that at an HTTP request level is by using a custom content type header : Content-Type:application/json;domain-model=RemoveItemsFromInventoryCommand or Content-Type:application/json;domain-model=CheckInItemsToInventoryCommand.

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • Hi guillaume31, I think I am also getting a chance to clear. For designing DAP, is it good to expose the behaviour of resource in such way as in the article you mentioned? or follow the level 3 REST API design as Mark pointed out? Should I choose the way that is easier for me? – PainPoints Sep 10 '15 at 14:29
  • 1
    The article also describes a level 3 REST API. Everything Mark said remains valid. The thing is, your commands don't capture user intent (*task-based UI*) IMO. They are very granular, going down to the field level. Thus you're forced to either shoehorn every single entity field into a resource, which I find impractical, or find another way, for instance through media types. But both would remain level 3 on the Richardson REST maturity model. – guillaume31 Sep 11 '15 at 07:12
  • Putting a command name in the URL or in the Content-Type header, how is it different ? Neither are about transferring state. – Arnaud Le Blanc Feb 12 '16 at 14:34
  • @arnaud576875 it's different because putting a command name in a URI would mean it's a different resource. Besides, REST isn't about transferring state but transferring *representations* that *capture the current or intended state of that resource*. And of course the examples I give capture the intended state. Their payload contains a bit of the future state of the resource, namely `{ count: X }`. – guillaume31 Feb 15 '16 at 21:21
  • @arnaud576875 but surely you can share with us your own ideal solution to the problem ? – guillaume31 Feb 15 '16 at 21:24
  • What about specifying the command type in a custom header? Would that be acceptable? Are there any major shortcomings? – edu_ Nov 19 '20 at 18:47