Imagine a REST endpoint (/employees) serving pages of employees in JSON HAL format.
An employee lives in a country, which resides in a continent.
For both countries and continents there are also separate endpoints.
The returned pages contain the typical _embedded field with the employee data.
The employee resource also contains the nested country resource.
This nested country resource also contains the _links.
In this case the output would be:
GET /employees
{
"_embedded": {
"employees": [{
"employee_id": 1
"name": "Mr. X",
"place_name": "London",
"country": {
"alpha2_code": "AU",
"name": "Australia",
"continent": {
"code": "OC",
"name": "Australia",
"_links": {
"self": {
"href": "http://localhost:8077/continents/au"
}
}
},
"_links": {
"self": {
"href": "http://localhost:8077/countries/au"
}
}
},
"_links": {
"self": {
"href": "http://localhost:8077/employees/1"
}
}
},
{
..
}
]
},
"_links": {
"first": {
"href": "http://localhost:8077/employees?page=1&size=10"
},
"self": {
"href": "http://localhost:8077/employees"
},
"next": {
"href": "http://localhost:8077/employees?page=2&size=10"
},
"last": {
"href": "http://localhost:8077/employees?page=8&size=10"
}
},
"page": {
"size": 10,
"total_elements": 71,
"total_pages": 8,
"number": 0
}
}
Is the nesting of the country (and also the nesting of continent within the country outputted in the correct way following the HAL specification.
In some other examples on the I noticed the following format:
{
"_embedded": {
"employees": [{
"employee_id": 1
"name": "Mr. X",
"place_name": "London",
"_embedded": {
"country": {
"alpha2_code": "AU",
"name": "Australia",
"_embedded": {
"continent": {
"code": "OC",
"name": "Australia",
"_links": {
"self": {
"href": "http://localhost:8077/continents/au"
}
}
},
}
"_links": {
"self": {
"href": "http://localhost:8077/countries/au"
}
}
}
},
"_links": {
"self": {
"href": "http://localhost:8077/employees/1"
}
}
},
{
..
}
]
},
"_links": {
"first": {
"href": "http://localhost:8077/employees?page=1&size=10"
},
"self": {
"href": "http://localhost:8077/employees"
},
"next": {
"href": "http://localhost:8077/employees?page=2&size=10"
},
"last": {
"href": "http://localhost:8077/employees?page=8&size=10"
}
},
"page": {
"size": 10,
"total_elements": 71,
"total_pages": 8,
"number": 0
}
}
UPDATED: second example now also clearly shows it is a paged response.
It uses nested _embedded resources.
Is there - in perspective of the specification - one approach better then the other? Or are the both valid?
Actually the HAL spec is pretty clear about when to use
_embedded:This has two implications:
The nested document that's supposed to appear under
_embeddedalso needs to be a representation of a linkable resource, i.e. it needs to be a resource on its own.The nested document placed in
_embeddedis considered a preview of the actual resource. Unless there's a dedicated resource for the nested document, don't put it in_embedded. If you're inclined to add aselflink to the nested document, it needs to / should go into_embedded.There's usually a connection between the keys used within
_embeddedand a link that appears in_linksof the same document.An example
Take the following document representing an order as example:
See how
itemsis an array of potentially complex object directly nested in the document. That means there's no separate resource representing the items. They're part of this resource.customeron the other hand appears both in the_linkssection, indicating there's a resource related to this one, whose semantics are defined by whatcustomermeans in the application domain. That samecustomeralso appearing in_embeddedbasically indicates: here's a preview of what the representation of the related resource looks like. The nested document can be completely identical to what you'd get if you followed the link. but it can also be of completely different shape to serve the clients needs accessing the current resource. E.g. instead of listingfirstnameandlastnameseparately, the embedded variant could only contain adisplayName, or a simple string version of an address that's a complex object in the actual resource's representation.The same applies to the
productnested inside the line item representation. The item might even have thedescriptionpersistently derived from the state of the product it was added. But what's listed initems.[0]._embedded.productcould essentially carry more in-depth information about the product the line item is pointing to. However, of course, the product is not "contained" in the line item.This approach enables what's described in the spec as Hypertext Cache Pattern. The client inspecting
_embedded.$rel.$interestingPropertyfirst and -- in case it's not finding it -- resorts to resolving the link and looking for$interestingPropertythere. That's a pretty standard procedure to implement and allows a server to gradually move properties into_embeddedto avoid the clients to need to lookup the related resource in the first place. John Moore demonstrates this approach in this talk (using HTML as media type but effectively the same pattern).The DDD side of things
Although REST -- and even more so HAL -- doesn't know anything about DDD, this distinction is pretty helpful when it comes to designing representations of DDD aggregates, as it allows to differentiate between nested, complex objects that are part of the aggregate (the line items in my example) and references to related aggregates (the customer in my example). The primary means to implement the latter are links of course, but very often you need to have access to a preview of the related resource (e.g. a master detail view on all orders for which you'd like to display the full name of the customer that placed the order). The concept of
_embeddedallows you to express exactly that.What also plays into this is the question of what you actually update if you PUT a payload back to the server. Naturally you want to limit changes made to the resource to the aggregate that's backing it and not span multiple ones. In my example that means you naturally wouldn't want to be able to at the same time change details about the order and change the customer's lastname as that change would span both aggregates, which you're supposed to avoid according to DDD. By moving the customer related data into the mediatype-owned
_embedded, the server can basically ignore the synthetic fields and only apply changes made to the natural ones.