Cqrs api v2

46
Commanding a More Meaningful REST API Brandon Mueller @fatmuemoo fatmuemoo.com

Transcript of Cqrs api v2

Commanding a More Meaningful REST API

Brandon [email protected]

Panhandle Web Techhttp://www.meetup.com/Panhandle-Web-Tech/

6:30 on the Third Tuesday of the Month@ Firebrand Media229 East Martin St, Suite 5Martinsburg, WV 25401

What is covered

● API Basics● API Good Practices● CRUD API Examples● Commanding API Explained● Commanding API Examples● CRUD and Commanding Coexistence

What is NOT covered

● Security or Authentication● HAL or HATEOAS● CQRS* or DDD*● Nitty-Gritty Implementation Details

● The farm is a home to people, places and things. These are the nouns in your API

● Nouns are also called resources or entities.● The nouns are represented in your API by the URL.● Like things inside of your farm, you can do things with

your resources.● You tell your nouns to do things via verbs. ● The HTTP spec come with built in verbs, among these

are GET, PUT, POST, and DELETE● These verbs are great for CRUD functionality.

Your API is Like a Farm

HTTP verb Resource URL Explanation

POST /barns Creates a new barn

GET /barns Gets all the barns on the farm

GET /barns/11 Gets the barn with the ID of 11

PUT /barns/11 Updates the barn with the ID of 11

DELETE /barns/11 Deletes the barn with the ID of 11

C

R

UD

Some Good Ideas to Follow:1. ALWAYS use plural nouns - keeps it simple2. Nest resources!

/barns/30/animals

/barns/30/stables/1/animals

3. Handle errors with a standardized response body4. Use the standard HTTP verbs5. Use appropriate HTTP headers6. Use appropriate HTTP response codes

Read The Spec

Status Codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.htmlHeaders: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Status Codes In a Nutshell

● 2xx - It worked● 3xx - Go away● 4xx - The client messed up● 5xx - The server messed up

Idempotent/Safe Methods

Idempotent methods can be applied over and over again and always have the same effect on the resource

Safe methods do not have any modifying effect on the resource

Idempotent/Safe MethodsMethod Idempotent SafeGET YES YES

POST NO NO

PUT YES NO

DELETE YES NO

PATCH NO NO

HEAD YES YES

OPTIONS YES YES

POST v PUT v PATCH

● POST is used to create an entity● PUT is used to update an entity where you

must send the entire representation of the entity as you wish for it to be stored

● PATCH is used to update an entity where you send only the fields that need updated

CRUDy Workflow

Client

Application Server

Client uses HTTP POST, GET, PUT and DELETEVerbs to CREATE, READ, UPDATE and DELETE

Create an Animal$.post({

url: '/api/animals,

data: {

name: 'Bob',

}

});

Server returns “201 created” status and a JSON representation of the newly created street, as well a link to the street{

id: 30,

name: 'Bob',

_links: { self: '/api/animals/30' }

}

Read Animals$.get({

url: '/api/animals,

});

Server returns “200 OK”, and a JSONrepresentation of the streets{

page: 1,

total_pages: 10,

items: [

{id: 30, 'name': 'Bob', _links: { self: '/api/animals/30' },

....

]

}

Update an Animal$.put({

url: '/api/animals/30',

data: {

name: 'Robert'

}

});

Server returns “200 OK” status and aJSON representation of the street,{

id: 30,

name: 'Robert',

_links: { self: '/api/animals/30' }

}

NOTE: Most implementations of the PUT verb require the entire representation of the entity. See HTTP PATCH verb for updating with a partial representation of the entity.

Delete an Animal$.ajax({

type: 'DELETE',

url: '/api/animals/30'

});

Server returns a ‘204 No Content’ status and should not have aresponse body. Any further GET requests to /api/animals/30 should return a ‘404 Not Found’ status

Awesome!● We can create some really awesome APIs

where we can create, read, update, and delete entities in a standard way.

● These standards have lead to some very powerful libraries that can help you create and consume CRUD APIs

Where is the Business Logic?Client

Application Server

Client uses HTTP POST, GET, PUT and DELETEVerbs to CREATE, READ, UPDATE and DELETE

● Sometimes this is what we want● When we create APIs we don’t always know the specific

use case● We just want to expose the data

Business Logic on The Client Side?

● A CRUD API is a really good way to expose meaningful data in a flexible way

● You make few assumptions on how the data will be displayed or used

● Fosters innovation and allows for really interesting clients

Really? Client Side?

● Stopping at CRUD functionality limits what your API can actually do

● Complex business problems should not rely on client side implementations

● Only implementing CRUD functionality can lead to an API that doesn't know what it does, it only knows about the data it stores

Wait, This SUCKS!

How do you go beyond simply creating, reading, updating, and deleting things?● How do you reroof a barn?● Clean a stable?● Castrate a goat?

Commanding API

Take a resources, add a command (verb) to the end, send data for that command in POST HTTP verb

○ POST /barns/11/reroof○ POST /barns/1/stable/2/clean○ POST /goats/133/castrate

Command or Resource?Command Resource

Verb Noun

You can only POST and sometimes GET never PUT or DELETE

All HTTP verbs are OK

Once a command has been submitted, it cannot be removed or updated (read only)

Most resources can be removed or updated

HTTP Verb

Command URL Explained

POST /barns/11/reroof Sends the command to reroof barn 11

GET /barns/11/reroof Meta data about reroof command and barn 11. Maybe the last time it was reroofed, whether or not we are currently reroofing

GET /barns/11/reroof/{cmdID} Meta data about the specific instance of the reroof command

Commands Are NOT First Class Citizens

POST /createBarnPOST /barns

POST /updateBarnPUT /barns/1

POST /cleanBarnPOST /barns/1/clean

Example: Reroof a barn$.post({

url: '/barns/11/reroof'

});Server responds with ‘200 OK’ status, and meta data about the command:{cmd_id: '123e4567-e89b-12d3-a456-426655440000'}

In this example, the command is executed and completed within the lifecycle of the HTTP request.

Wait: That’s Not The Whole Story

We use a Commanding Pattern because we are performing an action on a resource. In the real word, these actions can take time.

Reroof a Barn$.post({

url: '/barns/11/reroof'

});

Server responds with ‘202 Accepted’status, and meta data about thecommand:

{

cmd_id: '123e4567-e89b-12d3-a456-426655440000',

_links: {

status: '/barns/11/reroof/123e4567-e89b-12d3-a456-426655440000'

}

}

Reroof a Barn; Check Status$.get({

url: '/barns/11/reroof/123e4567-e89b-12d3-a456-426655440000'

});

Server responds with ‘200 OK’ status and data about the command{

shingles_complete: 5,

out_of: 1024,

}

Client

Application Server

Client sendsPOST command

Clients Checks Status URL until

complete, updating UI with progress

What About Querying?

● Sometimes a query request cannot be completed in a reasonable amount of time

● Sometimes you have to send a POST payload in order to send enough data for the server to process a query

● This is a special case of a command

Querying$.post({

url: '/chickens/search'

data: {

type: 'heritage'

egg_color: 'blue',

age: 'young'

}

}); Just like a command, we return ‘202 Accepted’ status, and return some meta data:{

cmd_id: '7ecba660-5032-11e4-916c-0800200c9a66',_links: {

results: '/chickens/searchResults/7ecba660-5032-11e4-916c-0800200c9a66',status: '/chickens/search/7ecba660-5032-11e4-916c-0800200c9a66',

}}

Getting Results...If you try to hit the results link before the results are complete:● the server will return the search results in whatever

state it is available● return a ‘203 Non-Authoritative Information’● a link to the status url● and a Retry-After headerUsing this pattern, you can expire content and return the 203 status code to tell the client to refresh the content

Getting Results...HTTP Request

Do we have the

resource?

203 With a Retry After Header And Status URL

200 OK

Has the data expired or still being created?

Check Status

Status

Is it done?

Separating Command and Queries...

Developing expressive and meaningful endpoints...

Using nouns and verbs that reflect what your API

actually does...

Proudly Inspired by Others

ENTERING BUZZWORD ZONE!

CQRS Martin FowlerCQRS stands for Command Query Responsibility Segregation. It's a pattern that I first heard described by Greg Young. At its heart is a simple notion that you can use a different model to update information than the model you use to read information. This simple notion leads to some profound consequences for the design of information systems.

http://martinfowler.com/bliki/CQRS.html

DDD WikipediaDomain-driven design (DDD) is an approach to software development for complex needs by connecting the implementation to an evolving model. The premise of domain-driven design is the following:1. Placing the project's primary focus on the core domain

and domain logic.2. Basing complex designs on a model of the domain.3. Initiating a creative collaboration between technical and

domain experts to iteratively refine a conceptual model that addresses particular domain problems.

http://en.wikipedia.org/wiki/Domain-driven_design

DDD and CQRS in My Own Words...● Domain Driven Design is a philosophy and methodology

in which your domain model should represent the business problems that your app is actually solving

● Command Query Responsibility Segregation is the strategy of separating commands from queries, as opposed to CRUD functionality which is represented in the same object. CQRS can help you follow the SOLID principle

DDD - Know Your Role!

● Talk to stakeholders and domain experts

● Strive for a ubiquitous language● Know the context in which your

API endpoints will be used● Use this knowledge to develop

your endpoints

CRUD API Commanding API

Create a Product POST /products POST /products

Update a Product PUT /products/{id} PUT /products/{id}

Mark Product as Active PUT /products/{id} POST /products/{id}/activate

Describe a Product PUT /products/{id} POST /products/{id}/describe

Order a Product ???? POST /products/{id}/order

Markdown a Product PUT /products/{id} POST /products/{id}/markdown

Review a products POST /products/{id}/reviews POST /products/{id}/review

Mark product as out of stock PUT /products/{id} POST /products/{id}/outOfStock

CRUDy v Commandy

CQRS and CRUD, Mutually Exclusive?

Commands Are NOT First Class Citizens

POST /createBarnPOST /barns

POST /updateBarnPUT /barns/1

POST /cleanBarnPOST /barns/1/clean

CQRS and CRUD Can CoexistPUT or PATCH is just an update command BUT:● Be very selective with what is allowed to be updated● It is not always appropriate to expose update

functionality for a particular object or property● If another command updates a property, updating that

property directly probably shouldn’t be allowedDELETE is a special use case command too

Brandon [email protected]

END BUZZWORDS