Cortex Implementation Patterns
Cortex Implementation Patterns
The patterns below are solutions for common problems our customers experience when implementing/customizing their Cortex. Most resources use a mixture of these patterns, but we have separated them in this document for simplicity.
- Takes the guesswork of when/why/where/how to create new resources.
- Pair your business needs to an established pattern.
- Get building implementations using existing patterns rather than spending time designing/discovering your own patterns.
- Understand how the JSON representations and URIs connect to the pattern.
- Resource Lookup (GET) - Retrieve a related set of data from your backend system through Cortex.
- Read From Other (RFO) - Read data from another resource.
- Link Strategy - Add a link to a resource on another resources' representation.
- Resource Update - Update a resource's status with data from a client application.
- Form - Provide customers with the ability to create something from information entered into a form. For example, creating an address for a profile, adding a cart to an order, adding an order to a purchase, searching for items, and so on.
- Selector - Provide customers with the ability to make selections, such as selecting a billing address to use for an order, the options for an item, the paymentmeans to use for an order, and so on.
Resource Lookup (GET)
The Problem: You need to surface up a related set of data to a client application through the Cortex.
How do I surface up a single set of related data through Cortex API?
This is the most common customization problem for Cortex. You have a set of related data you want to surface up to client applications through Cortex. For example, you have in-store promotions that alternate each day. Your store marketers change the promotions in their backend system and client developers need to display these promotions through their client applications.
If you were customizing another API, you would likely modify an existing resource that's related to promotions in order to surface up this data. However, this is a bad way to go about customizing a product. Elastic Path is continually updating the Cortex, so if you modify the OOTB source code your Cortex will break when you upgrade. Also—and this is the main reason—modifying the OOTB code to add some additional functionality to a resource defeats the purpose of Cortex's design. Cortex is designed as a set of small independent resources to promote design qualities like cohesion (single responsibility) and to promote extension by composition. These designs help to maintain a clean/clear/consistent design over time as well as minimize the need to make breaking changes to OOTB code.
The Solution: Resource Lookup (GET): Create a new resource with the Resource Lookup (GET) pattern to retrieve a set of related data from your backend system and surface it through a single resource in Cortex.
Resource URI
Resource JSON Representation
Below is a JSON representation for a resource that uses the Resource Lookup (GET) pattern. The data is sparse in this example, but the idea is that this type of pattern returns a single set of related data. For example, the cart resource uses the Resource Lookup (GET) pattern to surface up cart data in a JSON representation.
When to Use
You want to surface a related set of data to a client application through a single resource.
Cortex Resources using this Pattern
* Nearly every resource uses this pattern
Read From Other (RFO)
- You want to reuse another resource's functionality in your resource.
- You want a resource to depend on another resource.
- You want to keep your resources' functionality granular.
How do I reuse an existing resource's functionality in the resource I'm building?
Cortex's extension design methodology is to create new resources that augments existing behavior, not modify OOTB source code to get the desired behavior. This extension design helps maintain a clean/clear/consistent design over time as well as minimize the need to make breaking changes to OOTB code. OK, you say, but then how do you create a new resource that augments another resource's functionality without changing OOTB code? Skip to the solution below to see.
Another problem you may experience is that you're creating a resource that needs to depend on another resource. Take for instance, the totals resource. Alone, this resource doesn't do anything. It depends on other resources to function. For example, totals uses an RFO to read a carts's lineitems, then calculates the total cost of the items in the cart. How do you build out a resource like this that depends on other resources?
Another common problem you may be facing is how to keep your resource functionality granular. Good API design recommends you create a resource to do one thing, not a hundred different things. But how can you separate functionality when your building resources? Admittedly, it's easier to build out one resource that does everything. Take a look at Digital River's Shopping API: Product Offers Resource. This one resource does it all: add to cart, images, pricing, special product pricing, and product descriptions. Compare it to Cortex and you see that we have a number of different resource to provide this functionality: items, itemdefinitions, assests, prices, and taxes. How do we keep our resource functionality granular and at the same time utilize all the components together?
The Solution: Read From Other (RFO): Create a new resource with the RFO pattern to read data from another resource.
With a RFO, you don't need to reinvent the wheel with your resources. The RFO can retrieve data from another resource, so you don't have to build out that functionality.
The RFO pattern can also help keep your resource functionality granular. Take for instance the assets and items resources. The assets resource uses an RFO to read an item, get its ID and scope, then uses those details to retrieve the item's assets (media files) from the back end system. This is apposed to having one resource that does these two separate responsibilities. With granularity like this you can switch out the assets resource for some other system without it affecting the items resource.
Resource URI
The resource URI, identified at the end of the URI, gets the profiles representation. The extensionprofile resource consumes the profiles representation and then surfaces its representation.
Resource JSON Representation
Below is the JSON representation for the extensionprofile, which uses the RFO pattern. The extensionprofile resource reads the profiles resource's representation and then creates its own representation from the data. In the example below, the family-name and given-name have been read from the profiles resource, added to the extensionprofile's representation, and then surfaced up to the client application along with the profileId. In this way, the profiles' resources functionality has been extending without changing the OOTB code for profiles.
{ "self": { "type": "application/vnd.mycompany.extensionprofile", "href": "https://localhost:8443/cortex/extensionprofile/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq", "uri": "/commerce-legacy/extensionprofile/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq","max-age": 0, }, "familyName": "Harris", "name": "Oliver", "profileId": "gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq", "links": [ { "type": "elasticpath.profiles.profile", "rel": "profile", "rev": "extensionprofile", "href": "https://localhost:8443/cortex/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq", "uri": "/commerce-legacy/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq" } ] }
- Reuse an existing resource's data in your resource - For example, the rates resource uses an RFO to read the items resource's ID and scope to retrieve in order to retrieve the item's cost and billing frequency.
- assets - Uses and RFO to read an itemdefinition and then surface up the itemdefinition's assets.
- availabilities - Uses an RFO to read a cart's lineitems and then surface up the lineitems' availability.
- totals - Uses an RFO to read the items in a carts lineitems and then surfaces up the total cost of the items in the cart.
Link Strategy
The Problem: You want to connect the resource you're building with another resource, but using an RFO doesn't make sense.
How do I link the resources I'm building to related OOTB resources or even to my other customized resources?
Say for instance you're building a resource that provides crosscutting functionality to a number of other resources. Using the RFO pattern to read data from other resources and then serving that data up through your single custom resource would ruin Cortex's single responsibility design. Also, this code would be hard to maintain and upgrade. So how can you add your custom resource's functionality to other resources—both OOTB Resources and custom resources—in a sensible way?
The Solution: Link Strategy: Create a resource that adds a link to itself on another resource's representation.
Cortex recommendations resource is a crosscutting resource that provides functionality to the items and navigations resources. The recommendations resource uses a Link Strategy to attach links to the items and navigation resources. Using these links, client applications can follow them to see recommended products for specific items and navigations.
The best part about using a link strategy is that you don't have to modify the resource you're linking to. The links are attached dynamically so the resource being linked to doesn't even know it has a link. This is Cortex's secret admirer rule. This way you can build out relationships without having to worry about code upgrade paths.
Resource URI
You'll notice that this looks just like a regular URI to access profiles...and it is! Link strategies don't affect the resource's JSON representation, not the URI.
Resource JSON Representation
{ "self": { "type": "elasticpath.profiles.profile", "href": "https://localhost:8443/cortex/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq", "uri": "/commerce-legacy/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq", "max-age": 0 }, "family-name": "Harris", "given-name": "Oliver", "links": [ { "type": "elasticpath.collections.links", "rel": "addresses", "rev": "profile", "href": "https://localhost:8443/cortex/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq/addresses", "uri": "/commerce-legacy/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq/addresses" }, { "type": "elasticpath.collections.links", "rel": "paymentmethods", "rev": "profile", "href": "https://localhost:8443/cortex/paymentmethods/mobee", "uri": "/commerce-legacy/paymentmethods/mobee" }, { "type": "application/vnd.mycompany.extensionprofile", "rel": "extensionprofile", "rev": "profile", "href": "https://localhost:8443/cortex/extensionprofile/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq", "uri": "/commerce-legacy/extensionprofile/profiles/mobee/gy3ukmrygbaugljxiu4dmljtgjatgljvhfbdeljwgeyemrrsinatgoceiq" }, { "type": "elasticpath.collections.links", "rel": "purchases", "href": "https://localhost:8443/cortex/purchases/mobee", "uri": "/commerce-legacy/purchases/mobee" } ] }
- lineitems - Uses a link strategy to create a link to the carts' representation.
- orders - Uses a link strategy to create links to the purchase, carts, deliveries, and billinginfo representations.
- redommendations - Uses link strategies to attach links to items and navigations.
- addresses - Uses a link strategy to attach a link to a profile.
- Link resources to other related resources.
Resource Update
The Problem: You need to update a resource with data from a client application.
How do I update a resource with data from a client application?
Often, client applications need to update a resource's data based on information provided by the client application. Changing an address, password, profile, and so on, are all examples of updating data.
The Solution: Resource Update: Create a resource that updates its state with data from a client application.
Resources can accept PUT requests from client applications and execute write requests to update the data in the backend system.
Resource URI
The addresses resource accepts the body of the PUT request, a JSON object shown below, and updates the address in the backend system. Notice the <addressid> portion of the URI specifies which address to update.
Resource JSON Representation
{ "address": { "country-name": "CA", "extended-address": "", "locality": "Richmond", "postal-code": "V6qE4s", "region": "BC", "street-address": "14300 Riverport" }, "name": { "family-name": "Tiger", "given-name": "Tom" } }
- lineitems - accepts a PUT request to update the quantity of items in the lineitem.
- profiles - accepts a PUT request to update the customer's profile data.
- addresses - accepts a PUT request to update an address's details.
When to Use
You want to update a resources state with data from the client application.
Reference Example
Look at lineitems, profiles, and the addresses resources.
Form
The Problem: You need a way for client applications to create data in Cortex.
How do I create data in the Cortex API?
A common problem is enabling client applications to create data in Cortex. For example, creating a new customer, a new address, a new paymentmethod, and so on. Also, when you expose a capability like creating data in the API, how do you define what data is necessary for your resource to create it? For example, say you want to expose the ability to create a new user in your system. How do you identify what data customers need to fill out in order to create this new customer? Or, say for example you want to expose the ability to create a new address for a customer. How do you identify what the address requirements are?
The Solution: Form: Create a resource with a form to provide customers with the ability to create something in the Cortex from the data they've entered in a form.
Cortex uses forms to create data. For example, creating an address for a profile, creating a new customer, adding a cart to an order, adding an order to a purchase, searching for items, and so on all use forms. Client Applications GET the form, fill out the form's details based of customer input, and then POST the filled out form to the form's actionlink to create the data.
Resource URI
The form identifier is a Cortex standard used to indicate a form returns from the request.
Resource JSON Representation
{ "self": { "type": "elasticpath.addresses.address", "uri": "/commerce-legacy/profiles/<scope>/<profileid>/addresses/form", "href": "http://www.onlinestore.com/profiles/<scope>/<profileid>/addresses/form", "max-age": 0 }, "links" [ { "rel": "createddressaction", "href": "http://www.onlinestore.com/profiles/<scope>/<profileid>/addresses", "uri": "/commerce-legacy/profiles/<scope>/<profileid>/addresses" } ], "address": { "country-name": "", "extended-address": "", "locality": "", "postal-code": "", "region": "", "street-address": "" }, "name": { "family-name": "", "given-name": "" } }
- address - returns a form to create a new address for a customer.
- orders - returns a form to submit the order to a purchase
- search - returns a form to submit the search keyword to the search resource.
- registration - returns a form to submit to the registration resource to create a new customer.
- profiles - returns an email form allowing customers to create emails for their profile.
When to Use
You need a customer's input to perform an action such as creating a new customer, making a purchase, creating a billing address, adding a credit card to their payment, performing a search, and so on.
Reference Example
orders - uses a form as well as needinfos to ensure a customer has selected their billing address, payment method, and if necessary their shipping address and options.
Selector
The Problem: You need to provide a way for customers to make selections in the Cortex. For example, say you have an item in your storefront, such as an iPhone that comes in red, blue, or green, and you want to allow customers to pick the color. Or, say you have a number of shipping options, like FedEx, Canada Post, or Purolator, and you want to allow a customer to select one to use for an order.
How do I allow customers to make choices through the Cortex API?
The Solution: Selector: Create a resource with a Selector to provide customers with the ability to make selections, such as choosing a billing address, shipping option, a payment method, item options, and so on.
Cortex uses selectors
Resource URI
The selector identifier is a Cortex standard used to indicate a selector returns from the request.
Resource JSON Representation
{ "self": { "type": "elasticpath.controls.selector", "uri": "/commerce-legacy/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo/selector", "href": "www.onlinstore.com/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo/selector", "max-age": 0 }, "links": [ { "type": "elasticpath.collections.links", "rel": "chosen", "rev": "selector", "href": "www.onlinstore.com/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo/selector/profiles/<scope>/<profileID>/addresses/<addresseID>", "uri": "/commerce-legacy/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo/selector/profiles/<scope>/<profileID>/addresses/<addresseID>" }, { "type": "elasticpath.collections.links", "rel": "choice", "rev": "selector", "href": "www.onlinstore.com/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo/selector/profiles/<scope>/<profileID>/addresses/<addresseID>", "uri": "/commerce-legacy/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo/selector/profiles/<scope>/<profileID>/addresses/<addresseID>" }, { "type": "elasticpath.controls.info", "rel": "destinationinfo", "rev": "selector", "href": "www.onlinstore.com/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo", "uri": "/commerce-legacy/orders/<scope>/<orderID>/deliveries/<deliveryID>/destinationinfo" } ], "name": "destination-selector", "selection-rule": "1" }
- itemselections - provides a selector to select item options.
- shipmentdetails - provides a selector to select the orders' shipping option (i.e. Canada Post, FedEx, and so on) and a selector to select the destination address.
- orders - provides a selector to select the order's billing address.
- paymentmethods - provides a selector to select the payment method to use for the purchase.
When to Use
You want to provide customers with the ability to make selections, such as select an item's options, the orders' billing addresses, shipping addresses, and paymentmeans.
Reference Example
shipmentdetails resource - provides two selectors: one to select the orders' shipping option and another to select the destination address.