About Me

I write therefore I am a writer.

These words are mine, and although they might lack in grammar or grace, if you look beyond these words you will feel the concepts I have tried to express. 

Much of the content here is technical, relating to my history as a developer. My future is in technical architecture, content strategy, enterprise content and giving guidance in these areas.

I try to publish something new every two weeks alternating between topics.

Navigation
Social
« A method for writing content | Main | Day CQ5 Development Best Practices »
Sunday
Jan152012

CQ5 best practices for component development

Following on from my advice for best practices for developing with Adobe Day CQ5, here are some suggestions centred around component development - you should tailor these to fit your needs. They should help if you are unsure where to start. There's lots of technical documentation on the product itself and the core open source frameworks; elements of these are useful for developers, but there's scant information on methodologies or strategies you can apply when starting out. Some of this may also apply to other systems albeit with different no-go areas and so on.

Avoid copying components

Firstly, avoid modifying anything under /libs. Also avoid copying anything from /libs to /apps. There are a few reasons for this:

  1. If you upgrade you will hate yourself for doing this. Your boss and co-workers may hate you too.
  2. Changing /libs implies that you are either not going to version control it, or that you are going to. Either way leads to madness, unless you like code to go missing and have servers which behave differently or if you want to add half of the core product to subversion. 

Copies to /apps should be VERY limited for a few significant reasons:

  1. when you upgrade, you need to upgrade this code too if you want it to work or at the very minimum have the new features. 
  2. there's no way of quickly identifying these 'overlays'.
  3. if you overlay something that is key, things go sour very quickly post-upgrade.
  4. having two components with the same name in the same group breaks design mode in CQ5.4 - for me at least

Override the bare minimum

All components for paragraph systems should have a 'sling:superResourceType ' of 'foundation/components/parbase'. Development of derived components should be done using 'sling:superResourceType' where possible. An example might be creating a paragraph system that behaves differently. Say you want to make one where component display is inverted, so that new components are inserted at the top - this is a parsys with a 'sling:superResourceType' of 'foundation/components/parsys' and a customised script. There's no need to copy the whole component.

Component Organisation

A recommended structure for component definition is

 /apps/<site-id>/components/page/<component>
/apps/<site-id>/components/content/<component>
/apps/<site-id>/src
/apps/<site-id>/install 
/apps/<site-id>/widgets
/apps/<site-id>/nodetypes
/apps/<site-id>/templates/<template>

Of course, all of these are optional. Page rendering components explicitly defined in the templates are split from paragraph and non-whole-page components such as navigational bars. This is purely for the benefit of developers; it means it's possible to see at a glance if the component is used to render a whole page or if it might appear as part of a page - a useful distinction. It has another benefit, for example it allows a 'sitemap' paragraph component which lists in HTML format to exist as well as a machine-readable sitemap.xml page type for SEO - two very different things with the same name.

Composition of Page Types 

A process of decomposition into sub-scripts is applied to the page rendering code to create a solid foundation which will allow extension of the basic type into more specifc page components:

/apps/<site-id>/components/page/base
/apps/<site-id>/components/page/base/base.jsp
/apps/<site-id>/components/page/base/head.jsp
/apps/<site-id>/components/page/base/body.jsp
/apps/<site-id>/components/page/base/header.jsp
/apps/<site-id>/components/page/base/content.jsp
/apps/<site-id>/components/page/base/footer.jsp

The base component extends the foundation page, with a 'sling:superResourceType ' of 'foundation/components/page'. 'base.jsp' basically sets the page doctype, outputs the <html> tags and includes head and body. Subsequenty, body.jsp includes header.jsp, content.jsp, and footer.jsp.

Extensible Page Components

This is the minimum for a base component which is extensible. We can easily make this better by adding other override-able scripts, such as adding an analytics.jsp or a meta.jsp. From this start point we can create a new component with a 'sling:superResourceType ' of '<site-id>/components/page/base', and the only thing we need to create is content.jsp

/apps/<site-id>/components/page/twocolumn/content.jsp
/apps/<site-id>/components/page/twocolumn/left.jsp
/apps/<site-id>/components/page/twocolumn/right.jsp

This obviously a two column layout. In order to separate content from code we should create a new component with a 'sling:superResourceType ' of '<site-id>/components/page/twocolumn', which now needs no scripts at all. It will derive all configuration and code from the super type, leaving us free to change the super type if we want a different representation (so long as the paragraph systems have the same names). We can also override the two column layout if we so chose, by creating a script which does something then including the overridden script. For example, we could create a  /apps/<site-id>/components/page/contentpage/right.jsp script which displays a title component then includes the  /apps/<site-id>/components/page/twocolumn/right.jsp  script. This gives a great level of flexibility.

Content has a type

Remember that when content is being created, the component path is 'stamped' into the content so that Sling knows what script to use to render it. Always plan components so that an appropriate resource type is associated with the content. In our page example, it is much better that it is a 'content page' than a 'two column page' or even a 'base page' as the intent is that is it content. The fact that it displays in two columns is incidental, especially if in six months time a third column is added. It's much more future proof to use a layer of inheritance, but only if it doesn't add unnecessary complexity.

Favour composition over inheritance

Components is CQ5 should also favour composition over inheritance. If you want a list-image component for example, don't take a list component, copy it, and hack it to have an image. A reasonable approach is to create a new component and add the necessary components to it. When you upgrade CQ to a new version, you'll have no changes to make - it'll automatically use the new version. If you did it by creating a new component by forking code, you could find it difficult to get it working if there have been major changes to the original component.

I hope this guide has been useful. It's based on real world development practices and is the basis of any recommendation I would give at this time. 

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (4)

Great post!

I had a comment about "Component Organization". I too had learned to organize components by apps/<site-id>/components, but it always seemed funny to me. To me that means you are hardcoding a soft dependency to your content. By that I mean if I put a component in "apps/mysite/components", I am assuming that it will be used ONLY in a site called "mysite". For some components (especially page components) that may be appropriate.

But for many components, specifically ones you would see in the sidekick, I see it differently. When building components, I feel like you are really building a toolbox for your content author. That toolbox enables them as stakeholders to build whatever content/sites are necessary. They may decide they don't even want "mysite", but determine that a component is still applicable on another site. Hence the reasoning behind separating "app" from "site".

My thought is that it would be more appropriate to group components into apps that correspond to categorical similarities between the components. For example, maybe you have a set of components for social networking integration ("Tweet This" button, "Like" button, etc.). To me it would make sense to group them in "apps/social/components" and probably do the same with their sidekick group.

What do you think? I will admit that I've not tried this approach yet, but after working through a few CQ projects, it seems beneficial in theory from an architecture perspective.
31 Jan 2012 | Unregistered CommenterRyan Lunka
Hi Ryan. I'm happy that you enjoyed this, and it is an interesting comment on components that authors add to the page. I quite agree that unless it is specific for a site it shouldn't be in the /apps/site folder. However... there are many cases where sharing a component between sites directly makes it extremely hard to update the component - it would change both the output of the component and the authoring experience in all cases where the component is used.

I would suggest that if it's a common component, put it in /apps/common/components then make a new component under /apps/site/components and set a sling:resourceSuperType to inherit the common version. This way if you want 'site8' to use a totally different version a year after launching the site it's fairly trivial. It may even be that the revised version needs something special for the site it is being embedded in (for example, it needs to be wrapped in an extra <div/> tag) and that it can include the script from its super type.

Another reason to group like this is to make it simple to deploy - we can package a whole app instead of trying to unpick the dependencies. It saves a lot of time and red faces!

I like the idea of grouping social media components under /apps/social - it makes it obvious to the developer what to expect, and simple to guess what it does when looking in the repository.
1 Feb 2012 | Registered CommenterAntony
I like the idea of wrapping components as app-specific components. I hadn't thought about that strategy and I think it would prove useful. I still struggle a bit with the best way to build components to make them package well. It seems like there are always trade-offs. I know I personally can get hung up on trying to be too "object oriented' and not enough "content oriented" sometimes, but there are scenarios that call for both concepts.

Looking forward to future posts/discussions.
1 Feb 2012 | Unregistered CommenterRyan Lunka
There is little talk that I have seen of 'component-oriented' with regards to it being an engineering process in a way that can reasonably applied to CQ5 - I like that term; it sums it up really well. It can be difficult to mentally switch between the different mindsets needed to develop Java, JSP, Sling components, and Sling dialogs - all of which are quite different. Of course this doesn't include other activities like JavaScript work or considering the solution architecture as a whole both of which can add a huge mental strain too.

I'll revise this post with you suggestions soon, and make sure I take them into account when I update this for CQ5.5. Thanks for the feedback, it's always good to hear from another practitioner.
3 Feb 2012 | Registered CommenterAntony

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
All HTML will be escaped. Hyperlinks will be created for URLs automatically.