The power of modular development

A modern digital platform doesn't just get built and then sit serving content pages for years without any changes or updates. Any system where you aren't continuously updating it to meet the needs of the business quickly becomes a legacy system, and other uncontrolled "micro-sites" will start popping up in other places (and all the FXM in the world can't make up for that!).

The counterpoint to this however is that constant uncontrolled change to any system can result in a big ball of mud.  When your digital platform is a key organisational asset this might get even worse when you have multiple development teams on multiple releases or across multiple vendors all working on the system at once.  Even when delivering in a single stream using Agile principles your solution can get messy without a clear underlying architecture "skeleton" to which you can add meat.

Sitecore's support for modular page composition through placeholders and modules provides a very good platform for building modular, componentised solutions.    Sitecore's Helix architecture provides a good starting point for thinking about this in it's section on Modules, however we have found a few other concepts and practices are helpful when delivering larger projects.



Feature level of modularity

I have seen several Sitecore implementation projects get into trouble by trying to deliver on a user story by user story basis.  Designing user experience for a story independent of other related functionality can lead to an anti-pattern that I can "UX micro-optimisation".  UX can get micro-optimised when each individual interaction is designed and refined in isolation of the overall system.  Even when real user testing is done and the feedback used (after all the road to hell is paved with good intentions), if an interaction is not consistent with the way the rest of a system works the overall impact can be a degradation of user experience.  In addition the fact that a slightly different pattern or approach is used in each place can multiply the implementation effort and cost of a solution.

The approach I favour I like to call "feature modules".  In this approach we consider a functional area of the solution as a whole, and do a level of design to establish the key patterns for how that functional area works.  An example of a feature module could be something like "events" or "media gallery" or even "search".  A good feature module exhibits those key call signs of good architecture - high cohesion and low coupling.  That is all functions in the feature module tend to be closely related to each other, but have few dependencies with other feature modules.  These feature modules become the "epics" of our implementation, and we do at least a high level MVP UX design for each one as the corner-stone of our solution.

Furthermore as we deliver across feature modules we try to create core UX patterns that can be reused - things such as consistent approaches to calls to action, listings and search.  Getting these laid down in a way that prevents too much rework means thinking about the order of design and delivery of your feature modules so that perhaps you consider those likely to have similar patterns at the design phase before progressing any into development.  This might seem heretical to some agile practitioners, but to my mind the real aim of agile is to reduce waste by not building things you don't need - and building something without considering another related piece of functionality you know you need to build seems to me a definite path to waste!  The key here is to understand what the MVP is of each of the features that you need to deliver.

Once a feature module has been developed, the renderings it contains can be used in appropriate placeholders across the site and mixed-and-matched where appropriate to create a website greater than the sum of it's parts.

Supporting multiple development streams

If you have ever worked on a large code-base you will know that the key things that will start to trip you up after a while are changes that require you to touch code across multiple parts of the solution, or having multiple changes occurring in different parts (or even worse, the same part) or the code by different teams or developers trying to achieve different things.  Having to then deploy a monolithic artifact and hope that there wasn't an unexpected side effect (or spend weeks in regression testing) is not much fun at all!  Automated regression testing suites can help here, but aren't a substitute for good architecture!

One of the key advantages to this modular architecture is that is separates functional areas together into cohesive solutions that are likely to change together at a separate rate to other parts of the system.  We like to have each feature module implemented in it's own self contained solution that builds into it's own deployment artifact.  This results in a module that can be deployed independently, with full confidence about what parts of the site are and aren't being changed.  This is a very similar pattern to that used in Micro-service design where each micro-service is independent of others to reduce side effects of changes rippling beyond the micro-service boundary.  Both these concepts also align quite neatly to the Bounded Context concept in Domain Driven Design.

With a few fairly simple conventions it is also quite easy to create architectural guidance that will let different streams -- or even different vendors -- create and update feature modules separately with minimal risk to the solution.  The key factors to consider are:

  • Always use Sitecore patch files based on the module name to inject configuration for your feature module
  • Have a consistent approach based around module name for website folder for things such as views
  • A similar folder naming structure should be used for Sitecore items for templates, renderings and other Sitecore items. 
  • Use a module naming conventions as a part of your module namespaces and assembly naming.
With these standards in place, you can create a build artifact containing website files (config files, assemblies, views) and Sitecore items (we have recently made the jump from TDS to Unicorn for managing these) and easily deploy it independently into your solution.

Conclusion

I hope this has given some high level patterns that you can use as you are planning and designing your Sitecore implementation. Taking these patterns in conjunction with the Microservice pattern (with microservices aligned with and supporting feature modules where functional business logic needs to be delivered) will help ensure you are building not just for your current project/release horizon, but will have a solution that can be easily enhanced and updated over a number of years by multiple parties, and without getting dragged into a morass of regression testing and side-effects!

These concepts are also underpin our approach to delivering Sitecore digital platforms based on frameworks to support multi-instance rollouts with a shared code base.  This is a pattern with application to deployments for very large multi-national organisations, which I will aim to address in a future post.


Comments

  1. Very interesting reading and great reference. Thank you for sharing this.

    We've been building up and applying a shared modular & framework based approach to building Sitecore sites over the last 18 months
    and from a purely technical point of view it's been very interesting. Although I do wish you could have written this and open sourced Atlas 18 months ago! :)

    I really wish we had followed a micro-service style pattern and kept discrete chunks of functionality self-contained as you've discussed here from day 1.
    We started out with what we called our Foundation-Infrastructure ModuleFramework and SharedModule libraries that started small but rapidly turned into big libraries of shared utils, services, base classes, interfaces,
    custom rules, workflow actions, pipelines, renderings etc. that then got inter-woven throughout various sites.
    It wasn't until it was in production and used across multiple sites and Sitecore instances and we needed to change one small part to support a small new site that we realised our mistake ...

    A couple of other key learnings I've personally had are:
    - That we weren't nearly as consistent and diligent with branching,tagging and versioning than we needed to be.
    We're now starting to embed gitflow fairly consistently and ended up rolling our own versioning and tagging meta runners for Team City builds, although I now wish we had used this GitVersion(https://gitversion.readthedocs.io)

    - Packaging, versioning and dependency management for shared, generic modules with both server-side and client-side resources that only come together at deploy time into a multi-site Sitecore instance ... is hard
    We went with the following packaging approach per module:
    * a nuget package for deployment of server-side resources via script/Octopus to Sitecore instance
    * a nuget package for public interfaces & models that are exposed and can be referenced in up stream projects for compile time binding
    * a bower package for client side resources that is used when building bundled,minified css & js for a site.
    * on top of this is customised msbuild targets to exclude shared files and libraries (log4net,Glass,structuremap etc.) and execute gulp task for client-side resource building during web publish
    Which seemed nice and flexible early on but we all now agree is far too complex and difficult to manage!

    ReplyDelete
    Replies
    1. Thanks for your thoughts Michael, I think that controlling complexity is definately the #1 consideration for anything at enterprise scale - but agree that keeping client side and server side modularity separate is a hard problem to solve. I think that keeping everything separate until the last responsible moment and bundling/minifying/compressing CSS and JS at runtime is probably the answer but we haven't proven that yet.

      We've been GitFlowing for a number of years now, but I hadn't come across GitVersion before - will check that out.

      Delete

Post a Comment

Popular posts from this blog

Cloud hosting Sitecore - High Availability

Setting up TDS to work with Azure DevOps

Sitecore - multi-site or multi-instance?