Resource Oriented Architecture and Design

Ed Wentworth
10 min readMar 2, 2022

An overview of an approach to architecting and designing RESTful services

As an antidote to Service Oriented Architecture, I always liked the term Resource Oriented Architecture. It is an approach that conceptually decomposes an API in terms of Resources first instead of Services. I believe focusing on the resource first helps to build not only better architecture but also design of more robust APIs. So let me coin (re-coin) another: Resource Oriented Architecture and Design (ROAD). This term is not intended as a formalism, but just a useful label that encompasses an approach for defining networks of services that follow both specific architectural constraints as well as API design principles.

This is a big topic so information on this post will be pretty high level. Later entries may focus on some details. Here I will give an overview of some of the key concepts.

RESTful

From an architectural style perspective REST (Representational State Transfer) was originally described in Roy Fielding’s dissertation, and defines several architectural constraints, including:

  1. supporting a uniform interface where a resource has a standard identifier, standard representations of data, and is self descriptive (via metadata)
  2. client/server based
  3. composable in layers
  4. stateless
  5. cacheable

Another constraint is the tersely defined (at the time of Fielding’s dissertation as “ hypermedia as the engine of application state” subsequently coined by the de-facto abbreviation, HATEOAS). I like to consider this concept in terms of ‘links’ that relate to state transitions and/or relations to other resources. More on that later.

From an API Design perspective REST design centers on the concept of a resource. Conceptually, a resource is much like an object instance in Object Oriented Programming (OOP) that has state, methods that manipulate that state and relationships to/from other objects, but these resource ‘objects’ are defined through the lens of REST. Also a resource can be related to aggregates in a domain model (in terms of domain driven design DDD ) and thus resource oriented API design can follow the DDD design principles.

http implementation assumption

Although REST architectural style doesn’t specify implementation, to be pragmatic, the implementation of a resource is typically over http and further adapts to its implementation and additional constraints. Http is a client / server protocol that identifies http targets with a Universal Resource Identifier (URI). A client such as a browser or another service makes requests of server’s implementation of the resource and gets responses with expected payload structures that include headers representing metadata and may include a body as a representation of the resource state at that time. These representations are in a format according to specified media types. Resources are accessed via standard http request methods (aka verbs) that include GET, PUSH, PUT and DELETE. Requests can include an Acceptsheader field that specifies which media types are expected in the response, and all payloads that contain a body should include a Content-Type header field defining the media type of the payload’s body.

Resources

This is the key organizational concept from both an architectural perspective and a design perspective. Resources are informational, rather than behavioral, defining domain concepts that represent a specific part of the state of a system that can be accessed in common ways. Resources support a uniform interface. In the HTTP implementation of our REST architectural style, the resource is uniquely identified with a URI, its information (or its state) is exposed via resource representations conforming to http media types, and implements one or more standard verbs such as GET, POST, PUT, DELETE and others. This standard uniform interface is one key to simplifying a model into highly reusable and standardized APIs.

Since Resources are identified by URIs, resources are composable into layers : organized hierarchically using path elements in the URI, and represent a singleton like /myprofile, a collection of information like /books , or a specific instance of information (usually composed within a collection), each with its own unique identifier as part of the URI (like /books/33349444).

Requests and Responses

Resources support a client/server implementation via a request and response access model. Each request can be stateless but the resources themselves provide access via representations of the system (application’s) state. The state of the resource (and the application) is manipulated via requests to resource URIs using standard verbs, and the state of the resource is reflected in the response.

The request may include header information and possibly payload information depending on the request verb. The headers include metadata useful to the transport (e.g. authentication information), hints to the implementation of the resource on a server (e.g. the media types desired for representations), or information about the state of the resource itself at that time (e.g. links to manipulate state). The request payload conforms to the media types (generally specified by the accepts header) that can be a representation of the desired resource state.

The response includes headers, common status codes such as 200, 401 or 500, and also possibly a payload. If included, this response payload is typically a resource representation that is encoded according to the media type requested or negotiated, and includes data representing the state of the resource at a particular time, and may also include metadata to make the representation as self descriptive as possible.

Standard verbs are methods operating on resources. The domain can define these expected implementations or they may be specific to the resource or its media type. Some standard constraints include:

  • GET — typically a read request of a collection or specific instance responding with a representation of state. A get can have a query string that includes conditions to further refine the response such as filter conditions. It is idempotent, and its results should be cacheable. And it should be a safe operation, never affecting state.
  • POST — create request of an instance typically targeting the collection resource URI where the request’s payload would be the resource instance data to be created, and the response will include the specific URI identifying the created instance.
  • PUT — update request to that resource taking a request payload that includes the new state. It is Idempotent, but this can be tricky. See the section on cacheability below.
  • DELETE — remove resource instances. In an idempotent way, a return status code of success can be returned even if the resource no longer exists.

Constraints around the standard verbs help enforce/define this capability. GET, PUT and DELETE requests are typically idempotent. Which means, execution of duplicate requests will not have effects different from the first request. GET is a safe request, and never has any side effects. Further it should always return the same request whether cached or not. Will discuss this in cacheability section following.

Representations

Representations express the current or desired state of the resource in their body. A resource can have many representations. Representations of a resource are defined by media types that can be custom to the domain or augmentations of common types. In a class-based OOP analogy, these media types can be thought of as the resource’s class, defining the implementation of request methods, expected data and metadata, and the structure of the data representations. Headers contain metadata to make these representations as self descriptive as possible.

In most implementations representations typically conform to json or xml variants for parsing and do not necessarily constrain this format to specific domains. But using media types specific to the domain help separate definitions of the domain from instances of the data. A media type in this case is like an interface or class and the resource is like an object instance of that class. But like some object oriented systems, the object instance can conform to one or more classes or interfaces. So a domain specific media type might be combined with a generic media type like application/vnd.mycompany.myapp.v2+json.

Links (HATEOAS)

Resources can be self defining, including how state is affected by actions, and what relationships exist to other resources and actions. For instance representations may include links to itself or other resources that indicate relationships or describe state transitions that can occur from the current state. A link can contain a URI to a target resource, and it can include metadata that defines the semantics of the relationship as well as the media types desired. And powerfully, it could define the method(s) allowed from the state.

for example:

{
"name":"me",
"_links": [
{
"rel" : "self",
"ref", "/me",
"type" : "GET"
},
{
"rel" : "parent",
"ref" : "/mydad",
"type" : "GET"
}
]
}

The interesting part in designing these links is in naming the relationships. Like an ontology design, the relationship types are as essential as the subjects and objects they relate. In REST, you can attempt to standardize these relationship names. ‘Self’ is typically used to provide a reference to a specific instance, perhaps an item of a collections resource. In paginations ‘prev’ and ‘next’’ might be used. In transitions supporting a CRUD interface, perhaps links like ‘create’, ‘update’ and ‘remove’ might link to corresponding resources with method names (e.g. PUT, POST, DELETE). If an application knows the meaning of the ‘rel’ names, it can render those links meaningfully.

Cacheability

Resources are cacheable (to a degree). Idempotent request methods like GET should be cacheable. This is often not the case in implementations where the GET resource state might be changed. If I cached a resource via a GET, and then subsequently someone else did a PUT to modify it, would my cached resource be the same now as the uncached one? One way to enforce the cacheability and safeness constraints is to include a version int he cached URI, and assure all PUTs bump or invalidate the version. An ETag can be used for this purpose. Further such a versioning constraint can help assure PUT does not override state done perhaps from the last GET if needed to compose the PUT data. To enforce this optimistic locking strategy, the PUT request should include the version of the resource assumed for the PUT.

Resource Design

The focus of resources is informational, not behavioral. Rather than decomposing a system into units of behavior as might be done defining service methods in a SOA, a resource oriented approach focuses on the domain of information first and then maps methods to alter that state in a standardized way such as mapping to standard http request methods (verbs); this is key to simplifying a model into highly reusable and standardized APIs. This may be obvious for resources that naturally identify ‘things’ and simple CRUD (Create, Read, Update, Delete) semantics are implicit. But other services may not be so obvious. In designing services in a SOAs, it is also typical to create endpoints that represent an action or a behavior such as cart.checkout or user.authenticate. These can still be represented as a resource through the process of what some call reification: defining the behavior by the concrete information created and consumed.

For example, where a service can be behavior focused such a user registration service that registers new users, a resource oriented approach may define similar part of the system domain in an information first way such a a resource that represents the registrations themselves. So a SOA approach might have a /register endpoint, a ROAD approach might be /registrations resource that deals with information around new and existing user registrations: i.e. a Registration. This representation object can be created, update, found or even removed based on the standard verbs : POST, PUT, GET or DELETE. Once created, a specific registration can be identified by a specific item resource /registrations/{id} . This representation could include a link or attributes to its status to indicate its state thorough a workflow, and perhaps additional information collected along the way such as user profile information. And a resource representation can include links to itself indicating what can be done to that resource to transition its state, perhaps support of a PUT that can take updates.

As another example, imagine /checkout rather than a verb can be /checkouts that represent checkout objects that have addressable representations and state. A POST to /checkouts creates a new checkout instance that can be addressed via a GET to /checkouts/{id} and status through the checkout process can be viewed. Checkouts resource itself (/checkouts) can be queried via query parameters perhaps for the latest checkouts not completed or cancelled. Individual checkouts can be status checked, updated or cancelled through the /checkouts/{id} resource. This makes this resource naturally auditable (keeping history of checkouts regardless of status) and friendly to asynchronous flows (being able to poll current status through checkout workflows for instance). A specific checkout can contain dependent information through sub paths such as the shopping cart of items (/checkouts/{id}/cart) or payment types (/checkouts/{id}/payments) and contain links to other independent information such as details of the customer.

Domain Driven

ROAD incorporates concepts of domain driven design, and REST API designs can be derived from DDD. Since resource identifiers can be hierarchical, it is possible to organize REST endpoints (Resources) in higher level groupings that first represent ‘domain contexts’, or other logical groupings that are closed or near closed. Depending on complexity, some organizations of resources can be grouped by using a ‘service’ as a sub path, but likely without a representation, only as a grouping of a collection of logically related resources. Some resources are purely ‘value’ objects and do not have individually identified resources, like an ‘about’ page that describes information about the entire domain. But other resources represent individually identifiable objects (‘entities’) that are typically accessible by the URI pattern /{resource}/{identifier} where the ‘repository’ itself is addressable as a collections resource (the first path element) representing a collection of entities that share a common representation type (media type), and individual entity instances within that repository are addressable by adding the unique identifiers as the next path element. Operations on entities should be limited to what can be mapped to standard verbs: GET, POST, PUT and DELETE. And where this is impossible, the ‘action’ itself may be a representable entity as described above (reification).

Resources typically follow the Domain Driven concept of a root ‘aggregate’. Operations on these roots are done all at once and in isolation but may represent many parts that are ‘composed’ into one. So dependent objects related in ‘composition’ may be available in sub paths which can help to simplify or organize representations that do not have to be operated on at once. /users/{id}/profile for example is a composable part of the entity identified in the users repository and its information does not live independently of its owner user entity.

Summary

As mentioned, this is a big topic and I will post some breakout discussions and examples in following articles (I hope). But focusing on resources, not services first and combining principles of REST architectural style and REST design with a healthy approach of domain driven understanding I believe can lead to much better API design and implementation.

--

--

Ed Wentworth

30 Years in development at all levels, I still love the challenge. Love art, music and writing too. Cant wait to see the next 30 years!