Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Modeling Content Types for JCR

Posted on April 2, 2015 By Luis Fernandez

What makes a good content type in JCR? Not the fanciest diagram. Not the biggest list of fields. The best models start with the questions your authors and apps need to answer. If you are on Apache Jackrabbit or AEM or Magnolia or Hippo CMS, the gear is ready. The trick is shaping nodes so your queries are boring and fast.

Start from questions, not from forms.

Start from queries, not fields

List the questions your site asks every minute. Latest news by tag. Product pages by SKU. Teasers for mobile under a certain word count. Let those questions drive your node type and property map. In JCR you win when the answer is a straight SQL2 or XPath line with no cartwheels. Fields come later. Queries first. That way you keep selectors simple, you keep joins rare, and you get cache friendly paths that Jackrabbit Oak can index cleanly.

If you cannot query it, you will end up scripting around it.

Define types that carry intent

Use CND to express intent. Keep it small. Use mixins for cross cutting features like tagging, SEO, and publishing dates. Keep binary blobs separate with nt:file when possible so backups and replication stay sane.

// file: /apps/com.example/nodetypes.cnd
<'com'='http://example.com/jcr/1.0'>

[com:article] > nt:unstructured, mix:versionable
 - jcr:title              (String) mandatory
 - com:slug               (String) mandatory unique
 - com:summary            (String) multiple
 - com:body               (String)
 - com:tags               (String) multiple
 - com:author             (String)
 - com:publishedAt        (Date)
 - com:heroImage          (Reference) > nt:base
 + com:content            (nt:unstructured) = nt:unstructured protected

[com:taggable] mixin
 - com:tags               (String) multiple

[com:seo] mixin
 - com:metaTitle          (String)
 - com:metaDescription    (String)

Two things to notice. First, slug is unique so you can route by slug with a single lookup. Second, authoring teams get flexibility inside a protected child called content for future blocks without breaking the top level shape. This keeps your main query fields at the top and the messy bits tucked away. Mix in com:seo only where you need it. Keep the core type tight.

Schemas are guard rails, not a cage.

Examples you can query without pain

// a compact article node
/content/site/en/news/2015/launch
  jcr:primaryType = com:article
  jcr:title = "We launched"
  com:slug = "launch"
  com:author = "Ana"
  com:tags = ["release","product"]
  com:publishedAt = "2015-04-01T09:00:00.000Z"
  com:heroImage = <uuid-of-asset>
// SQL2 queries that match the model
// latest articles with a tag
SELECT * FROM [com:article]
WHERE 'release' IN com:tags
ORDER BY com:publishedAt DESC

// lookup by unique slug
SELECT * FROM [com:article]
WHERE com:slug = 'launch'

// author feed with date window
SELECT * FROM [com:article]
WHERE com:author = 'Ana'
AND com:publishedAt > CAST('2015-01-01T00:00:00.000Z' AS DATE)
ORDER BY com:publishedAt DESC

These run clean on Oak when you back them with property indexes. Do not over index. Index the fields you filter and sort on and leave the rest alone.

// Oak property index example under /oak:index
/oak:index/articleSlug
  jcr:primaryType = oak:QueryIndexDefinition
  type = "property"
  async = ["async"]
  propertyNames = ["com:slug"]
  declaringNodeTypes = ["com:article"]

Mixins beat inheritance chains

Do not stack deep parent types to share fields. That turns into mystery meat. Use mixin sets like com:seo or com:taggable. You can add or remove them without hard migrations. Your authors will thank you and your queries stay readable. You also get freedom to evolve without breaking stored nodes, which in AEM or ModeShape keeps content packages small and safe.

Shallow beats clever every time.

Versioning, binaries, and paths

Turn on mix:versionable for text heavy types where rollbacks matter. Keep big binaries as separate nt:file under a known folder like /content/dam or /assets so you can control caching and offload to a CDN. Choose a path scheme that matches your navigation and your cache keys. Year and month in the path is fine if your editors care about date archives. If not, keep it short and slug based. Remember that path read is cheaper than a wide query.

When in doubt favor stable paths and indexed fields.

Common traps and quick fixes

Do not lean on nt:unstructured for everything. That is short term easy and long term pricey. Give your hot content a real type with declared properties so the repo can validate and index. Also avoid giant multivalue bags for tags and facets if you need to sort. Store sort keys as their own fields. If search is a first class feature, mirror key fields into a dedicated search node or push to Solr and keep JCR for truth and fast lookups.

Model for the reads you do one thousand times a day.

Shape your node types by the questions you ask and your JCR will feel simple.

Software Engineering Technical Implementation

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes