Umbraco

Umbraco Heartcore as a Microservice: Lord Lamington returns

How does Umbraco Heartcore fit into a Microservices architecture?

Written by: Emmanuel Tissera

How could you use Umbraco Heartcore to sell delicious Australian treats? Emmanuel Tissera from Luminary is here to give you some witty insight into just that, with his fictional client Lord Lamington. Feast your eyes on this delicious example of Umbraco Heartcore in 'action'.

In a previous article on Skrift, I have taken you on a journey where you saw Umbraco Heartcore power an omnichannel experience in a fictional Lamington shop.

But how does Heartcore fit in with Microservices Architecture? Let’s find out!

 

Keeping ‘em safe

Lord Lamington, a trusted but totally fictional client, is concerned about how his fictional cafes could operate safely during the COVID pandemic. Unfortunately, there has been a downturn in business due to COVID restrictions in Australia, especially in metropolitan and regional Victoria.

Lord Lamington wants to allow customers to dine-in. He also wants to make sure that his employees and customers are safe.

But national, state and council directives keep changing constantly in response to the case numbers reported. Thus, it’s a challenge for cafe managers to make sure they are following the current directives.

Luckily, a conscientious developer in Melbourne has released a non-public web API which could be used to obtain this information based on the postcode and square area of a dine-in venue.

Lord Lamington wants to use this “safe numbers” API to allow his cafe managers to get timely information on how many patrons are allowed in each cafe. He wants to reuse cafe addresses stored in Heartcore (as seen in the figure below) so as not to duplicate content. In the future, there’s a possibility of making this information public to alleviate any fears his patrons might have of dining-in.

Microservices to the rescue

Microservices architecture arranges an application as a collection of loosely coupled services. In a microservices architecture, services are fine-grained and the protocols are lightweight.

In this particular client requirement, we are talking about two microservices:

  • Heartcore - Content as a Service (CaaS) - Used to retrieve a cafe’s postcode, state and square area
  • Safe Numbers API - Provides contextual information based on input

This implementation could be further enhanced by plugging in other microservices to the mix.

  • Bookings API - Allow customers to book a table in advance
  • Rosters API - Allow cafe staff to change shifts safely

Plugging in various microservices means that we don’t need to build one large monolithic application to handle everything. By plugging into various other microservices, we can use the best-of-breed for each task. This also means we don’t need to reinvent the wheel and code everything ourselves.

 

Content as a Service (CaaS)

Does it seem like an overkill to use Heartcore to retrieve a postcode and a square area? If it was the only task this Heartcore instance was used for, I would agree. But, in this case, Heartcore powers Lord Lamington’s website and some of the data (i.e. postcode and state) is already stored on Heartcore.

Adding in fields to capture indoor and outdoor square areas is logical and in the long term, would help in compliance and transparency.

A note on structured data

When building data structures (document types/content types), you could be easily tempted to store the cafe address as a single text area or rich text field. But doing this does not allow us to reuse data as we could in this example. 

At Luminary, we have transitioned away from a WYSIWYG mindset to a more structured content approach. With our experience in headless CMSs, we see this as a natural transition to better utilize the features of CaaS.

With structured content, you can do more. Once you have mastered the art of structured content, it’s quick, the content editor does not need to second guess how it will look in different contexts, it’s reusable (as we see here), it’s great for performance and to top it off, works well across omnichannel delivery.

 

Content Delivery REST API

Traditionally, we would use Heartcore’s Content Delivery REST API to retrieve the content we need. The REST API has multiple client libraries and lets us retrieve content in a technology we are comfortable with. Here's how it would look:

Request:

curl --location --request GET 'https://cdn.umbraco.io/content/type?contentType=cafe' \
--header 'Accept-Language: en-US' \
--header 'umb-project-alias: damazingnut' \

Response:

{
    "_totalItems": 8,
    "_totalPages": 1,
    "_page": 1,
    "_pageSize": 10,
    "_links": {  // truncated for brevity
 },
    "_embedded": {
        "content": [
           {
               // truncated for brevity
                "contentTypeAlias": "cafe",
                "name": "Melbourne CBD",
                "parentId": "2d17326a-5867-4763-a80b-bc9d28e69ad7",
                "sortOrder": 0,
                "buildingNumber": "Level 1",
                "addressLine1": "195 Little Collins Street",
                "addressLine2": "",
                "suburb": "Melbourne",
                "postcode": 3000,
                "state": "VIC",
                "indoorSquareArea": 100,
                "outdoorSquareArea": 144
            },
            // 7 items truncated for brevity
        ]
    }
}


Response size:

Total response size

2.5 KB

Body 

1.97 KB

Headers

544 B

 

This response is quite heavy compared to the small set of data we need to fulfil our requirement. Let’s see how a RESTful API response compares to a Graph API request response.

 

GraphQL

GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. It allows clients to define the structure of the data required, and the same structure of the data is returned from the server, therefore preventing excessively large amounts of data from being returned. 

Let’s find out how Heartcore's GraphQL API allows us to request data:

Request:

curl --location --request POST 'https://graphql.umbraco.io' \
--header 'umb-project-alias: damazingnut' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"{
  allCafe {  
    items {
      name
      postcode
      state
      indoorSquareArea
      outdoorSquareArea
    }
  }
}"}'

Response:

{
    "data": {
        "allCafe": {
            "items": [
                {
                    "name": "Melbourne CBD",
                    "postcode": 3000,
                    "state": "VIC",
                    "indoorSquareArea": 100,
                    "outdoorSquareArea": 144
               },
                // 7 items truncated for brevity
            ]
        }
    }
}

Response size:

Total response size

1.29 KB

Body 

804 B

Headers

515 B

 

With the limited data set we needed, the size of the response body (804 B) is less than half of what it was of the REST API response body (1.97 KB).

The GraphQL query seems to have a few magic strings and conventions which you may not be familiar with. This is where the GraphQL Playground on Heartcore comes to the rescue. I found the docs, schema, real-time error highlighting, syntax highlighting and query history quite useful when starting out with GraphQL on Heartcore.

GraphQL filtering

Moving from the REST API to the GraphQL API gave us the benefit of returning only the fields we need from a particular data type. 

Lord Lamington’s aim is to keep all his cafes COVID safe. However, he is more concerned about the cafes in the state of Victoria (VIC). Heartcore’s GraphQL Filtering and Ordering allows a developer to easily filter the dataset at the source rather than at the destination. 

Request:

curl --location --request POST 'https://graphql.umbraco.io' \
--header 'umb-project-alias: damazingnut' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query($state: String!){
  allCafe(where: { state: $state }) {
    items {
      name
      postcode
      state
      indoorSquareArea
      outdoorSquareArea
    }
  }
}","variables":{"state":"VIC"}}'

Response:

{
    "data": {
        "allCafe": {
            "items": [
                {
                    "name": "Melbourne CBD",
                    "postcode": 3000,
                    "state": "VIC",
                    "indoorSquareArea": 100,
                    "outdoorSquareArea": 144
                },
                {
                   "name": "Springvale",
                    "postcode": 3171,
                    "state": "VIC",
                    "indoorSquareArea": 60,
                    "outdoorSquareArea": 80
                },
                {
                    "name": "Geelong",
                    "postcode": 3220,
                    "state": "VIC",
                    "indoorSquareArea": 80,
                    "outdoorSquareArea": 100
                }
            ]
        }
    }
}

Response size:

Total response size

846 B

Body 

326 B

Headers

520 B

 

With filtering in place, the size of the body response has halved again to 326 Bytes from 804 Bytes when we retrieved the non-filtered data. Another advantage of filtering and ordering the data at the source is that the client application does not need to manipulate any extraneous data.

 

Combining Microservices

To recap, in order to implement Lord Lamington’s requirement for finding the safe number of patrons allowed in each cafe, we need to combine the following services.

  • Heartcore Content as a Service (CaaS) via GraphQL
  • SafeNumbers Service via a REST API

 

Client Application Structure

The beauty of combining microservices is that, in the future, other microservices could be combined to build new features without adding all the functionality to a single codebase or a monolithic application.

 

Lord Lamington is a happy man.

He was able to use his headless CMS for more than just running his website. Heartcore’s Content as a Service model provided him with the data he needed without duplication or double entry.

He was able to use a service which provided him with the safe number of patrons for each of his cafes during this pandemic. He did not have to build or maintain that service.

The ability to use GraphQL and the ability to filter and order data via GraphQL knocked Lord Lamington’s socks off.

Have you tried fitting in Umbraco Heartcore as a microservice in your own bespoke application? Take some inspiration from Lord Lamington and let us know how it goes.

 

In the meantime - as my home state of Victoria records its’ seventeenth `double donut` COVID day, I’m off to eat a Lamington donut myself. Yum!