RESTf API vs. REST + CQRS API

已查看 336 次
跳至第一个未读帖子

Arun Nair

未读,
2018年1月7日 13:11:272018/1/7
收件人 DDD/CQRS
Hi,
   I was thinking of how to make a RESTFul API more intention revealing. .  A common patter I see around various blogs on this is
that conventional REST API results in

 Ban a Player  -> POST /players.

But I were to change to a Command , I could use

 Ban a Player  -> POST /players/{ id }/banPlayer

The second one I feel is more intention revealing.

The common objection I get from the team is that the second one does not comply with start REST style.

Would like to hear your opinion on this.

Also currently I cannot move away from a RESTful API.

Would like to hear your thoughts on this.

-Arun

Greg Young

未读,
2018年1月7日 13:30:102018/1/7
收件人 ddd...@googlegroups.com
Why not go one step further and have 

/players/greg

200 ok

which returns either a document or a set of headers that describe how I can then ban a player? eg HATEOS. 

There is nothing that precludes this from such systems, hell you can even do pub/sub over it look at eventstore and the example here: https://github.com/EventStore/Sklaida

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.



--
Studying for the Turing test

Arun Nair

未读,
2018年1月7日 14:07:262018/1/7
收件人 DDD/CQRS
I could do that, but my question was more on style.

Let's say by calling /player/greq, I get a document , which describes how to ban a player. My question is should the  url  returned be:
/players/greg/Ban ?

The question arose while I was looking at slide 23 at https://www.slideshare.net/fatmuemoo/cqrs-api-v2

Thank you,
Arun


On Monday, January 8, 2018 at 12:00:10 AM UTC+5:30, Greg Young wrote:
Why not go one step further and have 

/players/greg

200 ok

which returns either a document or a set of headers that describe how I can then ban a player? eg HATEOS. 

There is nothing that precludes this from such systems, hell you can even do pub/sub over it look at eventstore and the example here: https://github.com/EventStore/Sklaida
On Sun, Jan 7, 2018 at 6:11 PM, Arun Nair <arun...@gmail.com> wrote:
Hi,
   I was thinking of how to make a RESTFul API more intention revealing. .  A common patter I see around various blogs on this is
that conventional REST API results in

 Ban a Player  -> POST /players.

But I were to change to a Command , I could use

 Ban a Player  -> POST /players/{ id }/banPlayer

The second one I feel is more intention revealing.

The common objection I get from the team is that the second one does not comply with start REST style.

Would like to hear your opinion on this.

Also currently I cannot move away from a RESTful API.

Would like to hear your thoughts on this.

-Arun

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.

Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.

Greg Young

未读,
2018年1月7日 14:14:492018/1/7
收件人 ddd...@googlegroups.com
This is not a CQRS question but a REST question.

To be fair the uri could be anything so long as it is followed from /player/greg more importantly is described as being to ban.

To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+unsubscribe@googlegroups.com.

Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.

Rickard Öberg

未读,
2018年1月7日 22:48:042018/1/7
收件人 ddd...@googlegroups.com
Hey,

2018-01-08 2:11 GMT+08:00 Arun Nair <arun...@gmail.com>:
> Hi,
> I was thinking of how to make a RESTFul API more intention revealing. .
> A common patter I see around various blogs on this is
> that conventional REST API results in
>
> Ban a Player -> POST /players.
>
> But I were to change to a Command , I could use
>
> Ban a Player -> POST /players/{ id }/banPlayer
>
> The second one I feel is more intention revealing.
>
> The common objection I get from the team is that the second one does not
> comply with start REST style.
>
> Would like to hear your opinion on this.

Great question! My system uses a REST API with backend implemented
using CQRS/EventSourcing using EventStore. The objection you note is
probably because none of them have actually read the REST thesis
(https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm), and
Roy's clarifications on his blog regarding HATEOAS (it's mandatory).
As Greg notes, you really gotta use hypermedia to get it right, and
the first style makes that really hard to do.

The rule of thumb is this: a good REST API is just an ugly website.
Meaning, there's a starting URL and from there clients can a) click
links or b) submit forms. Those are the only two actions. Hypermedia
tells what the URLs are, and also what method to use
(GET/POST/PUT/etc.). Just like using a web browser.

Given that, here's what I would do, guessing a bit about your domain.
/ (the API root) has a link to /players. On /players there's a search
form, which you can submit using id, or email, or username, or
whatever identifying info you want to use. Or one form for each, that
is ok too. If submitting that form yields only one result, then
redirect to it, meaning /players/{id}. On that page you would have the
information about the player, as well as links to all valid actions.
If a player is already banned, then there is no link for that. If the
player is not banned, then the page includes a link to a form for
banning that player. Client clicks that link, let's say, and get to,
for example: /players/1234?action=ban. This returns a form with all
the fields needed to ban a player, maybe a comment from the admin on
reason for banning, references to evidence, etc. The form would say
whether it's a POST or PUT, probably a POST, and the client can then
fill in the form and submit it. If form data is invalid return 400 and
show form again. If ban succeeds then do a redirect back to the player
at /players/1234.

That's how you would do it on a website, and since a REST API is just
an ugly website, that's a reasonable way to do it. You can vary some
of the details above, but that's the gist of it.

The reason I like /players/1234?action=ban rather than
/players/1234/ban is only because I prefer using hierarchies for
entity hierarchies only. Allows me to for example do
/players/1234/account and /players/1234/history, which would then not
clash with the forms I can do to perform changes on /players/1234.

REST URLs are opaque, so a perfectly valid way to do it as well is
this: ?type=player&id=1234 rather than /players/1234. From a REST
point of view that is completely arbitrary.

I've built my system according to the above, and it works really well
and is easy to change and evolve. We have changed URL structures
completely a bunch of times, with no changes to clients (like mobile
apps), because all they do is follow links and submit forms, so as
long as rels and form id's are stable, they are ok.

For content type, you can't use application/json obviously since it
has no support for links and forms (it would be like trying to use
text/plain for a website, doesn't really work). We went with
Collection+JSON, which so far has worked really well, and allows
clients to behave exactly as described above (follow links, submit
forms).

YMMV, but that's my take on building REST APIs with CQRS and
EventSourcing. There are some extra steps to consider because of the
eventual consistency of submitting forms that perform changes, but
nothing too complicated.

regards, Rickard

Ben Kloosterman

未读,
2018年1月7日 23:24:352018/1/7
收件人 ddd...@googlegroups.com
For external APIs I tend to go more rest style 

eg  
 Ban a Player  -> POST /players{id}    and post a player with banned = true..

This is because 
1)  its integration and you want chunky deep documents  to provide a consistent view .. 
2) Integration is all about sharing data , the less logic the better . 
3) You normally incur a  latency hit
4) The api tends to be small and a small CRUD API with little logic is easy to learn / implement.

For internal apis 

I tend to go  Ban a Player  -> POST /players/{ id }/banPlayer

The reason being
1) You need fine grained control
2) behavior is more important.
3) There are LOTS of calls so a method name adds a lot of value .

Regards,

Ben 





Ben Kloosterman

未读,
2018年1月7日 23:34:192018/1/7
收件人 ddd...@googlegroups.com
So to add I use 2 .. apis... and for internals I normally use  POST /players/banPlayer?id=Guid

Note in some cases you may find your internal apis need to transition to binary / tcp standards eg protobuffs or some micro services are already using it , ... this style makes that a lot easier.

Having a small api just for external tend to keep it very small as well - a very good thing .. meaning you may not have to go the HATEOAS path  ( but if you have a larger public path you should consider it) 

Ben 

Arun Nair

未读,
2018年1月8日 02:50:262018/1/8
收件人 DDD/CQRS
Thank you for the detailed explanation,, sounds logical.The 'action' part of the URL makes more sense too.

-Arun

Sébastien Dubois

未读,
2018年1月11日 14:22:362018/1/11
收件人 DDD/CQRS
Indeed this is a question about REST API design and I think it can be kept separate of the idea of using CQRS/ES. With REST you normally try to avoid representing actions/commands as separate URIs and rather represent those as properties on your rest resources to avoid URIs explosion and improve caching support of intermediary devices. This guideline is also related to the uniformity constraint of REST. It's much easier to understand/use an API that models actions as properties because you don't have to know that a .../ban URI exists.

In your "player" resource, you should have a "banned" property. Banning/"unbanning" should be done either using a POST (partial update) or PUT (full update), modifying the value of the "banned" property.

For example:
POST .../players/<player_uuid>
{
  "banned": true
}


Sébastien

Arun Nair

未读,
2018年1月12日 00:19:452018/1/12
收件人 DDD/CQRS
Well it is a REST API design question, but I was asking about it within the context CQRS, i.e., break up the API itself into Command and Query, and have them hosted on different app servers and access different data stores.  The Player aggregate will have distinct write and read models, and so will a couple of other aggregate roots.  These models will be influenced by mainly 2 contexts (registration and game play). 

The registration context does not have much of a requirement for scaling, but when a game or games are being played, scaling and performance are  important. The updates to the database will mainly affect the scores of the players in the game play context as opposed to the registration (i.e.players need to be registered (along with various other entries like  game hosting devices, sites and casinos spread over different parts of a city or even a state, zones within a casino etc) before being able to participate in a game).

Hence my question, within the context of CQRS would one host the 2 APIs on separate servers or on the same one. Maybe I am wrong, but it seems to me that here the API design becomes intertwined with CQRS. 

-Arun

Rickard Öberg

未读,
2018年1月12日 00:33:272018/1/12
收件人 ddd...@googlegroups.com
2018-01-08 15:22 GMT+08:00 Sébastien Dubois <lecht...@gmail.com>:
> Indeed this is a question about REST API design and I think it can be kept
> separate of the idea of using CQRS/ES. With REST you normally try to avoid
> representing actions/commands as separate URIs and rather represent those as
> properties on your rest resources to avoid URIs explosion and improve
> caching support of intermediary devices. This guideline is also related to
> the uniformity constraint of REST. It's much easier to understand/use an API
> that models actions as properties because you don't have to know that a
> .../ban URI exists.
>
> In your "player" resource, you should have a "banned" property.
> Banning/"unbanning" should be done either using a POST (partial update) or
> PUT (full update), modifying the value of the "banned" property.
>
> For example:
> POST .../players/<player_uuid>
> {
> "banned": true
> }
>
> Some more input:
> https://github.com/NationalBankBelgium/REST-API-Design-Guide/wiki/REST-Resources-Actions.

This document uses application/json, a content type which is not
compatible with REST (see 5.2.1.2 in the REST thesis). All suggestions
in the document seem to be based on this error, and so can be safely
ignored from the point of view of building a REST system. Some
assertions (like "translate" not being a resource) directly contradict
the REST thesis.

It seems like you are describing an (essentially) RPC system, not a
REST system. Which is a different thing altogether.

regards, Rickard
回复全部
回复作者
转发
0 个新帖子