Get off my lawn - A journey from Sitecore MVC to the JSS world

A journey from MVC to JSS

During Sitecore Symposium 2019 I gave a presentation about our journey as a company and developer community on moving from a Sitecore MVC mindset and specialization towards the Sitecore JSS world. This blog summarizes the most important take aways from this presentation.

Working together

One of the challenges we faced as a company was finding a good way of working together between front-end and Sitecore back-end developers. Coming from a distant past where we had front-end developers delivered static HTML, CSS and JS files and having a Sitecore developer transforming and migrating this into a Sitecore .NET solution, we evolved into an eco-system where front-end developers started working inside the .NET solution and worked with Razor files and more modern frameworks then JQuery such as AngularJS or React. This was fine for a while, but with Angular coming with newer versions and a need from UX perspective to deliver single page application experiences we needed to move to different solutions as the server side rendering world of Sitecore MVC was no longer accelerating our projects.

With Sitecore bringing headless into the playing field with their JSS implementation, while still being able to leverage all the content editing experience and marketing capabilities to our customers, we re-aligned our way of working together, to be able to leverage also the modern FE frameworks to deliver this much needed UX experience.

Sitecore first and contract first

After starting our JSS journey with a code first approach where Front-end developers were defining in the Sitecore manifest file the structure of the pages, content etc. We quickly converted to developing Sitecore first (as described in an earlier blog) to make sure we kept full control of our content tree and enabled our Sitecore developers to ensure an optimal experience for content editors and marketers was created.

To ensure our Front-end and Sitecore developers can work in parallel we decided to go for a contract first approach. In this step in our development lifecycle these two disciplines go over a design, split it up in components, placeholders, fields and field types, and based on this construct a Json contract. To interpret this contract in our Frontend code in disconnected mode we wrote a small custom proxy.
Based on this contract both tracks can start their development:

Sitecore:

  1. Create Templates
  2. Create renderings
  3. Create placeholders
  4. Create instance items:
    1. Assets
    2. Datasource instance
    3. Sample route
  5. Custom content resolvers if applicable
  6. Test through layout service whether returned contract matches designed contract


Frontend:

  1. Bootstrapping new component
  2. Implement updated manifest with contract
  3. Register placeholder
  4. Create and implement component html, and logic
  5. Finally test integrated once layout service call for sample route comes available from Sitecore developer

Contract first Json sample
{
  "id": "home-overview",
  "fields": {
    "pageTitle": "Welcome X"
  },
  "placeholders": {
    "pr-top": [
      {
        "componentName": "Header",
        "fields": {
          "content": "test

"
        },
        "placeholders": {
          "pr-header-right": [
            {
              "componentName": "MessageIndicator",
              "fields": {
                "label": "New messages :",
                "link": {
                  "href": "/notification"
                }
              }
            }
          ]
        }
      }
    ],
    "pr-main": [
      {
        "componentName": "TwoColumnWrapper",
        "placeholders": {
          "pr-two-column-left": [
            {
              "componentName": "MainNavigation",
              "fields": {
                "items": [
                  {
                    "name": "Account Overview",
                    "displayName": "Account Overview",
                    "fields": {
                      "icon": {
                        "url": "/sitecore/content/X/global-content/icons/account-overview",
                        "fields": {
                          "cssClass": {
                            "value": "icon-account-overview"
                          }
                        }
                      },
                      "link": {
                        "value": {
                          "href": "/",
                          "querystring": "",
                          "linktype": "internal",
                          "text": "Account Overview",
                          "anchor": "",
                          "url": "/X/Personal/Web/home",
                          "title": "Account Overview",
                          "class": "",
                          "target": ""
                        }
                      }
                    }
                  },
                  {
                    "name": "Perform Transaction",
                    "displayName": "Perform Transaction",
                    "fields": {
                      "icon": {
                        "url": "/sitecore/content/X/global-content/icons/transfer",
                        "fields": {
                          "cssClass": {
                            "value": "icon-transfer"
                          }
                        }
                      },
                      "link": {
                        "value": {
                          "href": "/transfers/perform-transaction",
                          "querystring": "",
                          "linktype": "internal",
                          "text": "Transfer Money",
                          "anchor": "",
                          "url": "/X/Personal/Web/home/transfers/Perform-Transaction",
                          "title": "Perform Transaction",
                          "class": "",
                          "target": ""
                        }
                      }
                    }
                  },
                  {
                    "name": "Personal Archive",
                    "displayName": "Personal Archive",
                    "fields": {
                      "icon": {
                        "url": "/sitecore/content/X/global-content/icons/messages",
                        "fields": {
                          "cssClass": {
                            "value": "icon-messages"
                          }
                        }
                      },
                      "link": {
                        "value": {
                          "href": "/personal-archive/personal-archive",
                          "querystring": "",
                          "linktype": "internal",
                          "text": "Personal Archive",
                          "anchor": "",
                          "url": "/X/Personal/Web/home/personal-archive/Personal Archive",
                          "title": "Personal Archive",
                          "class": "",
                          "target": ""
                        }
                      }
                    }
                  },
                  {
                    "name": "Personal Profile",
                    "displayName": "Personal Profile",
                    "fields": {
                      "icon": {
                        "url": "/sitecore/content/X/global-content/icons/user",
                        "fields": {
                          "cssClass": {
                            "value": "icon-user"
                          }
                        }
                      },
                      "link": {
                        "value": {
                          "href": "/personal-profile",
                          "text": "Personal Profile",
                          "anchor": "",
                          "linktype": "internal",
                          "class": "",
                          "title": "Personal Profile",
                          "querystring": ""
                        }
                      }
                    }
                  },
                  {
                    "name": "Open Account",
                    "displayName": "Open Account",
                    "fields": {
                      "icon": {
                        "url": "/sitecore/content/X/global-content/icons/add-account",
                        "fields": {
                          "cssClass": {
                            "value": "icon-add-account"
                          }
                        }
                      },
                      "link": {
                        "value": {
                          "href": "/open-account",
                          "text": "Open Account",
                          "anchor": "",
                          "linktype": "internal",
                          "class": "",
                          "title": "Open Account",
                          "querystring": ""
                        }
                      }
                    }
                  },
                  {
                    "name": "Services",
                    "displayName": "Services",
                    "fields": {
                      "icon": {
                        "url": "/sitecore/content/X/global-content/icons/settings",
                        "fields": {
                          "cssClass": {
                            "value": "icon-settings"
                          }
                        }
                      },
                      "link": {
                        "value": {
                          "href": "/vnext/settings",
                          "querystring": "",
                          "target": "",
                          "text": "Services",
                          "anchor": "",
                          "url": "/X/Personal/Web/home/Services",
                          "title": "Services",
                          "class": "",
                          "linktype": "internal"
                        }
                      }
                    }
                  }
                ]
              }
            },
            {
              "componentName": "LastLogin",
              "fields": {
                "label": "Previous login: "
              }
            }
          ],
          "pr-two-column-right": [
            {
              "componentName": "PageTitle",
              "fields": {
                "title": "Account Overview"
              }
            },
            {
              "componentName": "BankAccountFullList",
              "params": {
                "listType": "AccountOverviewList"
              },
              "fields": {
                "iconStyle": {
                  "value": "icon-style"
                },
                "noAccountsText": {
                  "value": "no accounts text"
                },
                "newAccountLink": {
                  "value": {
                    "href": "/vnext/login",
                    "text": "Link Name",
                    "anchor": "",
                    "linktype": "internal",
                    "class": "",
                    "title": "alt text",
                    "querystring": ""
                  }
                }
              },
              "placeholders": {
                "br-rich-text-button": [
                  {
                    "componentName": "RichTextButton",
                    "fields": {
                      "link": {
                        "value": {
                          "href": "/vnext/onboarding/single-or-joint",
                          "text": "",
                          "anchor": "",
                          "linktype": "internal",
                          "class": "",
                          "title": "",
                          "target": "",
                          "querystring": ""
                        }
                      },
                      "text": {
                        "value": "Complete your profile to 
\r\nStart your first deposit >"
                      }
                    }
                  }
                ],
                "br-pop-up": []
              }
            }
          ]
        }
      },
      {
        "componentName": "CookiePolicy",
        "fields": {
          "cookiePolicyText": "This site is using cookies to improve your experience. If you continue, you automatically accept the use of cookies for this site.\nRead more about our cookies in our Privacy Notice.\n"
        }
      }
    ],
    "pr-bottom": [
      {
        "componentName": "Footer",
        "fields": {
          "copyrightText": "This company is registered at the Chamber of Commerce (Kamer van Koophandel) under number xxx. 

",
          "items": [
            {
              "template": "footer-link-item",
              "fields": {
                "link": {
                  "href": "https://www.sitecore.com",
                  "text": "Disclaimer"
                }
              }
            },
            {
              "template": "footer-link-item",
              "fields": {
                "link": {
                  "href": "https://www.sitecore.com",
                  "text": "Privacy"
                }
              }
            },
            {
              "template": "footer-link-item",
              "fields": {
                "link": {
                  "href": "https://www.sitecore.com",
                  "text": "Terms of Service"
                }
              }
            },
            {
              "template": "footer-link-item",
              "fields": {
                "link": {
                  "href": "https://www.sitecore.com",
                  "text": "Rate Card"
                }
              }
            },
            {
              "template": "footer-link-item",
              "fields": {
                "link": {
                  "href": "https://www.sitecore.com",
                  "text": "Depositor Information"
                }
              }
            },
            {
              "template": "footer-link-item",
              "fields": {
                "link": {
                  "href": "https://www.sitecore.com",
                  "text": "Contact"
                }
              }
            }
          ]
        }
      }
    ]
  }
}


       
 

Lessons learned

We didn't expect it, but our developers, when given the space, were quiet open to broaden their scope. Frontend developers learned a bit about designing Sitecore layouts and items, and our Sitecore developers learned typescript and how to set up basic components on the Frontend. This meant in a team that was up to speed, the need to go contract first was even eliminated at some point, as a single person could bring forward an integrated solution between Sitecore and JSS components.
Lesson 1
When your solution/app grows to a significant amount of components you need a lazy loading concept implemented to make sure upon launching/initial load of your app, your user does not experience an unwanted delay. Luckily for the different frameworks supported by JSS these options are now available out of the box, you just need to start using them explicitly.

Lesson 2
Separate your code for Frontend and Sitecore/backend into different repositories to keep your folder clean and have an explicit decoupling.

Lesson 3
Benefit from Single Page Application UX possibilities. JSS enables you to use modern FE frameworks, but also adds additional complexity. Make sure you make use of the capabilities that are available to you, otherwise it may still be faster for you to develop your MVC solution.

Lesson 4
Enable parallel development by starting contract first. Going back to a 'waterfall' approach where your Frontend developer needs to wait for a Sitecore developer to finish the item definitions is a waste of efficiency and doesn't work nice in an Agile approach.

Lesson 5
During development keep an eye out for frequently (or on PR/CI) checking of your precompiled code. Nothing worse then on your QA environment finding out your frontend code uses the 'window' object, which breaks server side rendering.

Lesson 6
Empower your developers to optimize through custom content resolvers, new software architectures and reuse of open source packages. The JSS world opens up a whole can of new plugins, design patterns and reuse of what others have created, including the Sitecore community, make use of it!


Reacties

Populaire posts van deze blog

I Robot - Sitecore JSS visitor identification

Sitecore campaigns and UTM tracking unified

Sitecore JSS - Sitecore first