In a previous blog posts (https://deliverystack.wordpress.com/2020/07/21/contentstack-net-sample-entry-model/) I may have cast Contentstack Modular Blocks (https://www.contentstack.com/docs/developers/create-content-types/modular-blocks/) in a bad light. In fact, Modular Blocks are incredibly powerful and honestly one of the few differentiators between headless CMS providers from a .NET content delivery perspective. They aren’t difficult to model or implement.
A CMS may allow multiple Field Value(s) for a Field or repetition of a Group of Fields defined in a Content Type some number of times in an Entry. Any CMS should allow Field Values to contain zero or more references to other Entries. This is how most headless CMS solutions model nested data. The nested data may not actually appear within the representation of the Entry, but as data referenced by the Entry. This approach can provide advantages for management (separate locking/versioning/workflow/translation/publishing of the Entries) and can provide flexibility (change a single reference to change the entire solution) but can also presents CMS usability and challenges editing all of the Entries and development challenges bringing them all together. In my experience, developer productivity and CMS usability (along with realistic and well-defined requirements) lead to successful implementation and user acceptance which lead to (relative, potentially non-fiscal) RROI (Relative Return On Investment) increase, often including TCO (Total Cost of Ownership) reduction.
I think that every piece of data should have a primary URL, where that URL typically includes a host and path and can include a name and query string parameters, where at least one of those values can include a JSONPath. If a piece of data does not have a URL, how can it appear in search results? If it cannot appear in search results, is data of any value?
URLs typically correspond to data and service endpoints. With a CMS, content page URLs correspond to Entries. When the system or its data changes, URLs should not (unless it was given the wrong URL originally and a user intentionally changed a URL, and so forth). In addition to data from other systems, pages rendered from the data in the Entry associated with a given URL can incorporate data from Entries referenced by that Entry, including Entries that have URLs.
All of this indirectly supports the fact that I want separate Entries that an Entry can reference AND the ability to nest data within an Entry. Which brings us back to Modular Blocks, which support the latter. Modular Blocks are like Entries nested within parent Entries, where the parent Entries have identifiers (unique IDs, URLs, and other criteria) and each child Entry (Blocks) have a JSONPath that includes the identifier of its Field. Think of Blocks as Entries at JSONPaths nested within the JSON representation of the Entry that contains them.
To model a type of Block in a Modular Blocks Field, implement a (base class for the types of Blocks in the Modular Blocks Field and an enum to identify them if these types do not already exist and a) Block Model class to model the Fields in the Block just as you would implement an Entry Model class to model the Fields in a Content Type. To model a Modular Blocks Field in an Entry Model class, implement a property that exposes a list of the base class for the Block Models in that Field and a JsonConverter to create the right type of objects during deserialization.
The Contentstack .NET Model Generator (https://www.contentstack.com/docs/developers/dot-net/contentstack-net-model-generator/) generates all of the required C# classes for Entry Models and Block Models, including the JsonConverters. For several reasons, I prefer to code these classes by hand. One of those reasons is that I wrote a generic base class for properties that expose Modular Blocks Fields in Entry Models and another piece of infrastructure that does all of the JsonConverter work automatically. With a little further understanding, you will likely find Entry Models to be logically simple, incredibly powerful, and honestly relatively trivial to implement in .NET. For one thing, they make it very easy to pass specific types of models to ASP.NET partial views. The Block objects can even inform the view (or the layout or the nested partial or whatever) which partials to invoke. And you can let the CMS user select a partial to receive the Block Model…
I am in the process of writing documentation for Contentstack about all of this. If you would like to review draft documentation or discuss before its publication, please comment on this blog post or contact me directly.