Rest with DDD and CQRS

1,928 views
Skip to first unread message

Jeff

unread,
Dec 21, 2015, 3:27:34 PM12/21/15
to DDD/CQRS
Looking for any examples or references that show proper modeling of a rest api for the command side when using ddd.  Anyone know of any?  How do you expose a micro service that models an AR into a properly defined rest service api (layer 4 richardson maturity model)?

Thanks.

Kijana Woodard

unread,
Dec 21, 2015, 4:03:37 PM12/21/15
to ddd...@googlegroups.com
"...a micro service that models an AR into a properly defined rest service api..."

There's an awful lot of assumptions packed into this statement. Care to unpack it a bit?

For instance, I'm still not sure what a "micro service" is or how it is different from any "well modeled service". And then there is deciding on a definition for "service"....

On Mon, Dec 21, 2015 at 2:27 PM, Jeff <je...@magentatech.com> wrote:
Looking for any examples or references that show proper modeling of a rest api for the command side when using ddd.  Anyone know of any?  How do you expose a micro service that models an AR into a properly defined rest service api (layer 4 richardson maturity model)?

Thanks.

--
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.
For more options, visit https://groups.google.com/d/optout.

Danil Suits

unread,
Dec 21, 2015, 4:40:03 PM12/21/15
to DDD/CQRS
Have you seen Jim Webber's "Rest in Practice" talk: https://vimeo.com/43612850 ? The DDD part starts about 35 minutes in.

With the assumption that the client and server are communicating over HTTP, my current, incomplete, understanding:

The Rest part ends at the application layer - the command you want to run *is* the resource.  The domain model hides in the black box where you can't get at it - all of the interaction with the domain model is achieved by manipulating the document system.  (Which is fine, the domain doesn't care about application state anyway.)

Therefore: every command has a unique identifier generated by the client, that identifier becomes part of the path of the command resource, and the client PUTs a representation of that command to the resource until it gets some acknowledgement back from the server.  You can later DELETE that same resource to acknowledge that the client received the reply to the put; or in some asynchronous workflows as an attempt to cancel that command (?).  GET on the command resource can be monitored for command status, I suppose.

As far as I can tell, it's reasonable for the representation of the command returned by the PUT to include relation links to allow the application to move on.  There are potentially some interesting things you can do here, as the locations in a PUT response can invalidate caches along the way, which might be handy.  But the PUT response itself can't be cached.  Still, on the happy path, the command resource is only going to be touched once, so one might as well take advantage.

If you don't want client generated identifiers itself, then sending a POST to a resource to get a new resource location back is an option.  That's probably the right answer if you want to use signed or opaque locations (ie, deliberately undermining the client's attempts to guess the uri).  Of course, you similarly have the option to simply POST the command, and letting the endpoint create the right subordinate resource for you (POST doesn't promise idempotent, so I'm not keen on that answer myself).

Danil Suits

unread,
Dec 21, 2015, 5:14:04 PM12/21/15
to DDD/CQRS
Proofreading fail: the Webber talk is at https://vimeo.com/41763224

Jeff

unread,
Dec 21, 2015, 5:59:30 PM12/21/15
to DDD/CQRS
yeah i noticed that. I did a search by name and it found it though.
this seems to be an interesting approach.

Jeff

unread,
Dec 21, 2015, 7:13:18 PM12/21/15
to DDD/CQRS
ok forget trendy microservice BS for now.

basically im looking for ways to design a rest api that is well formed.
I'm sort of intrigued by this approach where he is passing the command as an etag.
http://www.infoq.com/articles/rest-api-on-cqrs

thoughts?

Michael Ainsworth

unread,
Dec 21, 2015, 7:27:27 PM12/21/15
to DDD/CQRS
I've used REST on a number of different projects, but although I'm relatively new to CQRS, I'm inclined to think that using REST with CQRS is counter-intuitive.

1. REST maps URLs to aggregates. Sometimes an aggregate needs to be recomposed. E.g., one aggregate later gets split into two aggregates, or two aggregates get joined into a single aggregate. E.g., instead of "/employee/[EMPLOYEE_ID]/payment" the URL endpoint becomes "/payment/[PAYMENT_ID]". You not only need to update your commands, but the URLs that map to those as well. Clients might suddenly see HTTP 404 messages when previously they'd be receiving another HTTP status code. Breaking clients is frustrating, especially if they're mobile and need separate deployment from the main web application. A single "/api/v1/command" entry point means you can use a Factory to instantiate the Command object, and then delegate the command to the appropriate handler. Likewise, a single "/api/v1/query" entry point means you can use a Factory to instantiate a Query object and delegate it to the correct query handler. If a command becomes obsolete, it is automatically handled (e.g., with an appropriate HTTP 4xx client error).
3. The HTTP verbs (POST/GET/etc) are rather generic. When I first started with REST, most JavaScript implementations couldn't send anything other than POST and GET, so commands like "PostponeSubscription" or "InvalidateParkingPermit" don't really make sense. I guess this is part of the famous "language translation blunts communication and makes knowledge crunching anemic" statement, although this terminology is only used by developers ("Hey Frank, I'm getting an HTTP 503 on the POST to /payments" vs. "Hey Frank, I'm getting an HTTP 503 when issueing an AuthorisePayment").
4. If your REST controllers become command handlers, then you're starting to couple parts of your application (the command handlers) to the HTTP protocol. If management decides in-office staff require a Desktop application (for rendering a 3D CAD application, or something else that you might find easier to do natively), then you have to remap everything for another protocol (e.g., raw TCP ports using Protocol Buffers or similar). Keep the implementation detail (HTTP) as lightweight as possible.
5. Rest "commands" are very CRUDy. E.g., a "PUT /contact/1234" means "Update the current state of the contact 1234 with this JSON/XML data". Definitely more CRUD-sounding that CQRS-sounding, and is also rather confusing in terms of Event Sourcing (if you're using it).

More points that the above, but you get the general gist. I don't like REST with CQRS. "REST" stands for "REpresentational State Transfer". That is, it communicates state using a variety of representations (XML, JSON, etc). I personally see it is an obstacle on the command side, where you're requesting a change of state, but using it on the read side may be more appropriate (e.g., especially if your C machine and Q machine are two separate machines).

Ben Kloosterman

unread,
Dec 21, 2015, 10:01:49 PM12/21/15
to ddd...@googlegroups.com
my 2c

REST and  DDD are very far apart and it maps poorly .,. REST favors CRUD and will force your domain into CRUD like behavior, You need at least a  large domain to REST layer .

CQRS and read model does map well to REST. For commands it ok but you either have to have each command being an end point eg  Customer/NewCustomerApplicationCommand  or do some custom serialization eg SendComnand ((string type , string body)[]) and process them yourself  eg foreach command deserailize based on type.  The issue here is command is many types there could be hundreds though there are many ways to handle this .  The many end points is probably the rest way and is simpler with a lot of grunt code. 

Ben

Kijana Woodard

unread,
Dec 21, 2015, 10:46:47 PM12/21/15
to ddd...@googlegroups.com
"1. REST maps URLs to aggregates..."

Not really. ReST maps urls to resources. Those resources have no intrinsic relationship to aggregates or any other architectural decisions.

"...couldn't send anything other than POST and GET"

In general, I avoid PUT and DELETE anyway. I avoid delete because, well, http://udidahan.com/2009/09/01/dont-delete-just-dont/. I avoid PUT because the emphasis in the http spec is "full replacement of the resource" rather than idempotency. If it was just the latter, I'd probably mostly use PUT.

"For commands it ok but you either have to have each command being an end point eg  Customer/NewCustomerApplicationCommand..."

Separate resources for commands is good. Here are two approaches.
1. POST /account/123/payments
2. POST /payments /* account info in body */

Given hypermedia, you can have the server direct clients to post commands directly at the relevant [micro]service. You can change these choices at will. There are standard http responses if you need "long running commands". There are also standards to redirect to the "target resource". 

You can use hypermedia [aka ReST] with many server implementation styles. Some parts of the system could be CRUD, some CQRS, some ES, etc, etc. All of that could be behind a hypermedia facade. To the client, there is only the hypermedia server.

I would use hypermedia more often, but the popular server frameworks make CRUD over rdbms tables the path of least resistance. Any other choice requires focus, effort, and determination.

Ben Kloosterman

unread,
Dec 22, 2015, 1:45:44 AM12/22/15
to ddd...@googlegroups.com
1. POST /account/123/payments
2. POST /payments /* account info in body */

Yep with  these is you don't have different commands for each you would normally have 30  so you end up with

POST /account/123/payments/addpayment 
POST /account/123/payments/cancelpayment
POST /account/123/payments/addpaymentmethod

etc etc

with POST /payments   you can put details in the body but then you need to manually determine the type and serialize and cast it , which has the advantage of sending multiple commands and the disadvantage of poor error reporting,

Both are workable but its not like the read model , though even the read model will be more like 
/account/overdueaccountview 


Very good point though some parts of any system should be CRUD especially admin / grid edit.

It is obvious though that REST is CRUD and we are just putting a square peg in a round hole and should be careful it does not affect the rest of the design  abstractions like these tend to leak and i see that too often, it with jason is however the only nice way if your  use javascript clients.

Ben

Greg Young

unread,
Dec 22, 2015, 6:10:14 AM12/22/15
to ddd...@googlegroups.com
see HATEOS

You can absolutely build a restful client over the top of a domain
model (or with CQRS)
> --
> 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.
> For more options, visit https://groups.google.com/d/optout.



--
Studying for the Turing test

Ben Kloosterman

unread,
Dec 22, 2015, 7:31:57 AM12/22/15
to ddd...@googlegroups.com

And i did say it was workable , most of us have used it , If you know what your doing , there is no issue , its just its fundamental nature and how it was designed was REST . Pretty much most of the tooling , examples and most of the devs  just create/update/delete methods by id , the mid level ones add more f fine grained business methods..and then all that leaks into the domain. 

 I know HATEOS , though i have not used it , but how does having a link change anything ?  Its just sugar/ convenient lookup for the client , no different to a servicelocater service( which can be standard REST)  and note unlike other directories such as UDDI ( and its interesting to know why it and similar ilk failed)  it tends to encourage multiple small calls rather than chunky ones. 
.
Regards,

Ben 

Greg Young

unread,
Dec 22, 2015, 7:42:58 AM12/22/15
to ddd...@googlegroups.com
"
I know HATEOS , though i have not used it , but how does having a
link change anything ? Its just sugar/ convenient lookup for the
client , no different to a servicelocater service( which can be
standard REST) and note unlike other directories such as UDDI ( and
its interesting to know why it and similar ilk failed) it tends to
encourage multiple small calls rather than chunky ones."

myDocument {
//some account information
link {
rel : "open"
href : "http://mydomain.com/accounts/1235566/open"
},
link {
rel : "suspend"
href : "http://mydomain.com/accounts/1235566/suspend"
},
link {
rel : "close"
href : "http://mydomain.com/accounts/1235566/close"
}
}

Now you can build your commands inside those methods

There are also ways of expressing things that are needed. Yes
sometimes using hateos is jumping through hoops but you can pretty
easily define a restful interface over the top of a cqrs model.
Basically the http code becomes the "client" and it is just an adapter
to the outside world

João Bragança

unread,
Dec 22, 2015, 7:43:02 AM12/22/15
to ddd...@googlegroups.com
That's kind of the point. Complete decoupling is achieved when the client doesn't know anything other than the mediatypes and a bookmark uri.

--
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.
For more options, visit https://groups.google.com/d/optout.

Kijana Woodard

unread,
Dec 22, 2015, 9:48:18 AM12/22/15
to ddd...@googlegroups.com
"Basically the http code becomes the "client" and it is just an adapter
to the outside world"

"Complete decoupling is achieved when the client doesn't know anything other than the mediatypes and a bookmark uri."

+1 to these.

For internal apps, one can debate the value here because the server side tooling doesn't help [or actively fights]  hypermedia. For external apis, there is a very strong case to be made because you have zero control over client upgrades. When [not if] you realize you've made mistakes in your design, every bit of flexibility you've given yourself is a win. Of course, it's not a silver bullet, etc, etc.

Minor:
POST /account/123/payments/addpayment 
POST /account/123/payments/cancelpayment
POST /account/123/payments/addpaymentmethod

I'd probably go with

POST /account/123/payments
POST /payments/abc/cancel
POST /payments/abc/payment-method

POSTing to a collection implies add.

I'm ambivalent between 
POST /payments/abc/cancel
and 
POST /account/123/payments/abc/cancel

This avoids the "magic endpoint" that has a large switch/if statement trying to figure out what was sent.
The reason this is minor is, if hypermedia, the client isn't constructing urls anyway. That means I can spend less time trying to perfect my url space and more time worrying about the messages.

To illustrate the flexibility here, you could also do something like:
POST /cancelled-payments /* body = { paymentId: 'abc' }

Effectively, this is where you send payment cancellation commands. You then have to work out relevant responses given the use cases you're trying to solve. Choices.

Hypermedia looks like CRUD because a lot of digital ink has been expended writing that narrative. Tooling vendors push that as well because it's something they can package as a "magic bullet" and it looks great in demos. Further they make it easy to "serialize objects" [probably from an ORM] which means your "api" becomes nothing more than a way to tunnel sql statements over port 80. Et voilà: all this work results [once again] in tight coupling and a fragile system.

It doesn't have to be used that way, but the whole world will try to make you think it does.

Ben Kloosterman

unread,
Dec 22, 2015, 9:13:41 PM12/22/15
to ddd...@googlegroups.com


Hypermedia looks like CRUD because a lot of digital ink has been expended writing that narrative. Tooling vendors push that as well because it's something they can package as a "magic bullet" and it looks great in demos. Further they make it easy to "serialize objects" [probably from an ORM] which means your "api" becomes nothing more than a way to tunnel sql statements over port 80. Et voilà: all this work results [once again] in tight coupling and a fragile system.

It doesn't have to be used that way, but the whole world will try to make you think it does.

Yes , exactly my point .  You can make it work but a lot of people do it the "standard" way ..and you will be the odd man out. It doesn't  have to be tightly coupled you can have lots of DTOs separate  but similar to your domain objects.

I was doing a bit of introspection where my objection lies , i have used it successfully a few times ..you can post and get many messages from / to a single end point if you want in rest.. its not  discovery , i do use tcp/udp a bit but that's not where rest sits. I think its the soft factors and how most people use it as i said before i prefer chunky hierarchical messages which actually work better in rest as you have less end points . And the style / structure naturally points you to single record by id.   Oh yeah odata  http://www.odata.org/  "OData - the best way to REST"

Ben

Kijana Woodard

unread,
Dec 23, 2015, 11:00:51 AM12/23/15
to ddd...@googlegroups.com
"...and you will be the odd man out."

That's a fair reason to do things "normally". You don't have to explain it or argue about it. Sure, you have the same problems you've always had...

Not a big fan of OData. I view it as "we don't really know what you want, so here query the db". To protect the system, you have to put limits on what can be requested [take, sortby, etc], which forces out of band documentation. That said, for systems where you control the client and the server - sure. If it's not a high volume system and you really just want to "put something out there" - sure. Sometimes you have to publish in order to find out what people actually want/use. Breaking changes loom, but hey, we didn't get into software because it's stable.

--

Damian Hickey

unread,
Dec 27, 2015, 2:27:07 PM12/27/15
to DDD/CQRS
>  I avoid PUT because the emphasis in the http spec is "full replacement of the resource" rather than idempotency. If it was just the latter, I'd probably mostly use PUT.

Hmm, I've been using PUT in my command handling lib for the latter reasons. Now I'm rethinking that. hmm

Kijana Woodard

unread,
Dec 27, 2015, 5:02:12 PM12/27/15
to ddd...@googlegroups.com
TBH, you could just ignore the letter of the spec unless you need to be interoperable with unknown equipment over the internet.

I *think* the biggest risk is some intermediary using the PUT value as a replacement in its cache. For commands, that's a non-issue.

From: Damian Hickey
Sent: ‎12/‎27/‎2015 1:27 PM
To: DDD/CQRS
Subject: Re: [DDD/CQRS] Rest with DDD and CQRS

Damian Hickey

unread,
Dec 29, 2015, 7:09:43 AM12/29/15
to DDD/CQRS
It's working just fine thus far. Then again, I'm not doing Rest at this point.... I agree with assertion that a Rest API is client itself.

Derick Schoonbee

unread,
Dec 30, 2015, 4:59:13 AM12/30/15
to DDD/CQRS
Just my 2c - it's driven by the needs of your WebAPI audience:

I would look at use cases your particular audience (Public Web API, Mobile devices, Micro Service, cUrl). A User Agent (REST Client like a browser or cUrl etc) does Operations on Resources (Urls). You can design resources to map to "any concept". You do not need to be CRUDy since you can map to your view models (mostly GET), Domain Layer AND Application Layer (PUT, DELETE, POST etc)

Fine grained operations:

POST /order/132/items => {ItemId:123, Quantity:345 } or {--lineitem-hypermedia--} => Maps easier to AddLineItemToOrderCommand
PUT /order/132/items/123/qty => {Qty:124} => ChangeOrderLineItemCount
DELETE /order/132/items/123 => {Reason:X} => RemoveLineItemFromOrder
PUT /order/132/status => {Status:Cancelled, Reason:Abandoned} => CancelOrder
DELETE /order/132 => {Reason:Abandoned} => CancelOrder

Operations for more granular documents could be:
PUT /order/132/items/123 => {<--lineitem-hypermedia-->} => UpdateOrderLineItem (Maybe not a UL term.. so propably use Application Service)
POST /order/132/items {<--lineitem-hypermedia-->} => UpdateOrderLineItem
POST /order/132 {<--order-hypermedia-->} => Lots of mappings and will probably map to Application Layer

The problem with fine grained operations is that when your domain model changes it will impact your API more than with more granular operations. For example which properties do I need to send for a specific Commands as opposed to just sending a LineItemHyperMedia.

I would opt for operations per entity (which is part of the UL) like OrderLineItem but not too large like a whole order document with child line items. I would consider this more web friendly (less chatty). Maybe one approach to start with is to move from fine grained to more granular operations but I would keep an eye on the explosion of custom media types. Take a look at http://sookocheff.com/post/api/on-choosing-a-hypermedia-format/ and the RESTBuck example below.

A good article on getting the gist of it is the RESTBucks example! "How to GET a cup of coffee" http://www.infoq.com/articles/webber-rest-workflow. There are some good tips in the book  "REST in Practice: Hypermedia and Systems Architecture" where they work on the same RESTBucks example.

Derick Schoonbee

unread,
Dec 30, 2015, 7:55:05 AM12/30/15
to DDD/CQRS
Oh, I see there is this too where they touch REST and CQRS : https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling

Kijana Woodard

unread,
Dec 30, 2015, 11:10:48 AM12/30/15
to ddd...@googlegroups.com
"DELETE /order/132 => {Reason:Abandoned} => CancelOrder"

Minor nit: unfortunately, you're not supposed to include a body with DELETE. Many web servers and intermediaries strip them out.

--

Greg Young

unread,
Dec 30, 2015, 11:38:02 AM12/30/15
to ddd...@googlegroups.com
You can quite easily do it with a header.

Kijana Woodard

unread,
Dec 30, 2015, 11:43:35 AM12/30/15
to ddd...@googlegroups.com
You mean put a payload in the header such {'Reason': "just because'}?

I generally just POST rather than trying to get tricky.
DELETE carries the right semantics for the API, but generally, I'm not actually deleting the server models, so I live with it. Same with PUT. I only use for cases where it's actually "full replacement". It's unfortunate the spec authors weren't a bit more flexible here.

At the end of the day, you can break the spec if you can control the consequences.

Greg Young

unread,
Dec 30, 2015, 11:46:19 AM12/30/15
to ddd...@googlegroups.com
No:

MyOperationReason : "Some known reasons"

where a raw delete no header means a default reason.

On Wed, Dec 30, 2015 at 4:43 PM, Kijana Woodard

Derick Schoonbee

unread,
Dec 30, 2015, 12:02:29 PM12/30/15
to DDD/CQRS
Well, DELETE is kind of broken as you said (but rfc2616 does not disallow it) due to draconian intemediaries / proxies / firewall...

I've worked around it with POST /order/132?override-post=DELETE (or something similar) that the client will generate in hypermedia links which makes this transparent. I've done some code than then rewrites the HTTP Request object.. but make sure you re-caculate checksums (sha1 for example) if you use this hack in your backend.

Damian Hickey

unread,
Dec 30, 2015, 12:08:02 PM12/30/15
to ddd...@googlegroups.com

X-HTTP-Method / X-HTTP-Method-Override may help...

http://fandry.blogspot.nl/2012/03/x-http-header-method-override-and-rest.html

You received this message because you are subscribed to a topic in the Google Groups "DDD/CQRS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/dddcqrs/CrDAI2RrLNQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to dddcqrs+u...@googlegroups.com.

Greg Young

unread,
Dec 30, 2015, 12:12:38 PM12/30/15
to ddd...@googlegroups.com
Thanks for the link!

Will look at implementing this in event store (we already support many
of the X- but have not run into a use case for these)

Greg Young

unread,
Dec 30, 2015, 12:14:42 PM12/30/15
to ddd...@googlegroups.com
Message has been deleted

Derick Schoonbee

unread,
Dec 30, 2015, 6:16:02 PM12/30/15
to DDD/CQRS
or DELETE /item/x?reason=Because

João Bragança

unread,
Dec 31, 2015, 4:56:46 AM12/31/15
to ddd...@googlegroups.com
Something to keep in mind is that the query string is a first class citizen of the URI, so that /item/x?reason=Because is not the same resource as /item/x. Something to keep in mind if you have to worry about intermediaries.
Reply all
Reply to author
Forward
0 new messages