Loose coupling is one of the longstanding best practices in software design. Decoupling your backend from the frontend comes with many advantages – both from an architectural and performance perspective.
Loose coupling allows a great flexibility, enabling you to make changes to individual components without affecting the entire system. This is important in web development as it lets you update or replace specific features without disrupting the entire website. Loose coupling supports scalability by allowing you to scale individual components or services independently. As your web services grow, you can allocate resources more efficiently to different parts of your application.
However, introducing loose coupling can sometimes lead to increased complexity in the communication between modules. This might require the implementation of additional communication mechanisms, which could impact performance if it's not designed carefully.
Designing a system with proper loose coupling, requires careful planning and consideration. If not done correctly, you might end up with inefficient communication patterns or dependencies, which could hinder your goal of building a faster web. In an improperly implemented loose coupling, you also risk introducing subtle bugs related to communication, data consistency, or synchronization. To avoid these issues, rigorous testing and validation are essential.
How to implement this pattern
When implementing this pattern, you must first decide if you want two or three layers. The presentation layer is a given, as you obviously need a frontend. But you have to decide whether you need to decouple the actual content editing application (i.e. the CMS) from the content delivery API.
If you need to scale or if you have many different data sources, a three-layer architecture is most likely the way to go. But it does come with added complexity, which we'll dive into in a minute. As you might already know, Enterspeed is specifically developed with the three-layer architecture in mind, so we'll base the steps below on a three-layer architecture.
A common way to implement this pattern is to listen to update events on the CMS and then ship the content to a cache/API layer:
- Listen to update events – either in process or via webhooks
- Transform the content to match frontend requirements – either in process or in a separate application
- Set up infrastructure for fast data store for content delivery API
- Ship transformed data to content delivery data store
- Set up infrastructure for auto scaling content delivery API
- Develop and deploy content delivery API
- Set up infrastructure for multiple geographical regions
You also need to think about backup, observability, and deployment pipelines.
How to implement with Enterspeed
The first thing you need to do is send your data to Enterspeed. The following code snippet is a JavaScript example using the Enterspeed Ingest API.
1// Ingest example in JavaScript using the Enterspeed Ingest REST API
2
3 const ingestToEnterspeed = async (sourceEntity) => {
4 const url = `https://api.enterspeed.com/ingest/v2/${sourceEntity.id}`;
5 const response = await fetch(new Request(url), {
6 method: "post",
7 headers: {
8 "Content-Type": "application/json; charset=UTF-8",
9 "X-Api-Key": "[EnterspeedApiKey]",
10 },
11 body: JSON.stringify(sourceEntity)
12 });
13 return response.json();
14};
15
16const mySourceEntity = {
17 id: 12,
18 url: 'https://mysite.com',
19 type: 'contentPage',
20 properties: { title: 'My headline' }
21};
22
23ingestToEnterspeed(mySourceEntity)
24 .then((data) => {
25 console.log(data);
26});
Then you use Enterspeed's schemas to model the ingested data into the desired output model. In the below example, a 1:1 property mapping of the ingested source entity is done:
1// contentPage schema with a 1-1 property mapping
2
3/** @type {Enterspeed.FullSchema} */
4export default {
5 triggers: function(context) {
6 context.triggers('cms', ['contentPage'])
7 },
8 routes: function(sourceEntity, context) {
9 context.url(sourceEntity.url)
10 }
11 properties: function (sourceEntity, context) {
12 return sourceEntity.properties
13 }
14}
To fetch the data in the frontend application, you can now use Enterspeed's Delivery API:
1// JavaScript example of calling the Enterspeed Delivery REST API to fetch views by url
2
3const call = async (query) => {
4 const url = `https://delivery.enterspeed.com/v2?${query}`;
5 const response = await fetch(new Request(url), {
6 headers: {
7 "Content-Type": "application/json",
8 "X-Api-Key": process.env.ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY,
9 },
10 });
11 return response.json();
12};
13
14const getByUrl = async (url) => {
15 const response = await call(`url=${url}`);
16
17 return {
18 ...response.route,
19 ...response.meta,
20 };
21};
And finally in your HTML view you can populate the view using plain JavaScript or any frontend library or framework like React or Vue.
1<!-- Simple HTML page that loads data from Enterspeed and populates the title and headline -->
2
3<!DOCTYPE html>
4<html>
5<head>
6 <script src="api.js"></script>
7 <script>
8 getByUrl('https://mysite.com')
9 .then((data) => {
10 document.getElementById('headline').innerHTML = data.title;
11 document.title = data.title;
12 });
13 </script>
14
15 <title></title>
16</head>
17<body>
18 <h1 id="headline"></h1>
19</body>
20</html>