Tag Archives: Sitecore Helix

Sitecore Helix- Modules that need to reference another module in the same layer Part 2

One module which is dependent on 2 or more modules

This is the second part in a 3-part series on dependency, if you have not read part 1 please read it first.

When a single module is dependent on 2 or more other modules in the feature layer there are few ways to solve this issue:

  1. Introduce an abstraction and implemented the abstraction in the project layer.
  2. Introduce an Abstractions in the foundation layer.
  3. Move the module to the project layer.

The Metadata Challenge

A typical example of a module, which can be dependent on 2 or more modules, is Metadata. In addition to providing keywords, taxonomy, etc. metadata is generally responsible for generating the title element in the head section and typical requirements could be as follows:

  1. If it is a news article, use the news title.
  2. If it is a blog post, use the blog post.
  3. If it is a product page, get the product title from the database.
  4. Otherwise return the item display name.

Metadata is a good example where implicit/soft dependency typically creep in, i.e. the metadata module needs the title for a given item and it uses the Sitecore field from the news module, blog module, product module, etc.

Solution 1 – Introduce an abstraction and implemente the abstraction in the project layer.

The only information that the metadata module needs that it can not determine itself is the title for a given item. Therefore, it is necessary to introduce an interface that satisfies that requirement, for an item give me the title. It is in fact very simple, see below.

namespace Feature.MetaData
{
	internal interface ITitleRepository
	{
		string Get(Item item);
	}
}

The context in the project layer is well suited to implement the ITitleRepository interface, as the project layer is responsible for aggregating/mediating the functionality provided by the feature and foundation layers.

Therefore, the next step is to implement ITitleRepository in the project layer, by getting the relevant information from the features (i.e. news, blogs, products) and provide the default behavior of returning the Display name if all else fails (see the example implementation below).

namespace Feature.MetaData
{
namespace Project.Context
{
	class TitleRepository : ITitleRepository
	{
		public string Get(Item item)
		{
			if (item == null)
				return string.Empty;

			// we assume that each repsository returns null, if the item is ot of the correct type...
			string value;
			//is the item a news?
			var news = _newsRepository.Get(item);
			if (!string.IsNullOrEmpty(news?.Title))
				return news.Title;

			//is it a blog post?
			var blog = _blogRepository.Get(item);
			if (!string.IsNullOrEmpty(blog?.Title))
				return blog.Title;

			//is a blog post?
			var product = _productRepository.Get(item);
			if (!string.IsNullOrEmpty(product?.Name))
				return product.Name;

			//default return display name...
			return item.DisplayName;
		}
		readonly ProductRepository _productRepository = new ProductRepository();
		readonly NewsRepository _newsRepository = new NewsRepository();
		readonly BlogRepository _blogRepository = new BlogRepository();
	}
}}

The last step is to use dependency inject to inject the implementation from the project layer, for the interface ITitleRepository.

Solution 2 – Introduce an Abstractions in the foundation layer

In some situations, it might be appropriate to introduce an abstraction in the foundation layer, but only if more than one feature depend on the abstraction. If only the Metadata is using the abstraction it is not sufficient to introduce an abstraction in the foundation layer.

In addition, you should consider why was the abstraction not identified and or present already, it could be a mistake but be sure.

See Part 1, where I have a step by step guide on how to implement this.

Solution 3 – Move the module to the project layer

Whilst this is an easy solution I do not recommend it.

The purpose of the project layer is to aggregate functionality provided by the feature layer and not provide functionality. For example:

  • Page Types module – determine which features are shown on given page type.
  • Context module is responsible for determining the context for each request
    • for example using dependency injection to decide which implementation should be used for any abstractions.

In addition the project is the least stable layer, and the majority of the Metadata functionality in my experience is very stable and similar across all projects, the only variation is how the title is generated and this is not enough to warrant it being moved to the project layer.

I hope this was helpful, and please continue to part 3.

Sitecore Helix – Modules that need to reference another module in the same layer Part 1

If you have not read my previous Helix and Modular Architecture post, I suggest you give it a quick read before this series on dependency.

In software, dependencies can either be explicit or implicit. Examples of explicit dependencies are when one assembly references another assembly. Implicit dependencies, sometimes referred to as soft or weak dependencies, are for example string references to Sitecore fields. See part 3 for more details on implicit/soft dependencies.

Dependency between modules typically happens in the feature layer and modules in the feature layer should not reference each other.

There are conceptually 3 groups of dependency issues:

  1. One or more module(s) dependent on one module.
  2. One module dependent on 2 or more modules ( see part 2).
  3. Modules that depend on each other (See part 3).

One or more module(s) dependent on one module

This is the most typical/common dependency issue to resolve. What to do when one or more modules need to reference another module (illustrated by the diagram above). There are a few solutions to resolve this issue:

  1. Consider that the modules boundaries/responsibilities are incorrect.
  2. Introduce an Abstraction in the foundation layer and keep the concrete implementation in the feature layer.
  3. Move the module to the Foundation Layer.
  4. Each module defines an interface and then a introduce a class in the project layer to mediate.

Solution 1 – Consider that the modules boundaries/responsibilities are incorrect.

This is a good solution when the module boundaries are not correct and or the modules are too small and have almost no responsibility. It is a common problem where there is a tendency to introduce new module/feature for each view.

For example, the breadcrumb and system menu should be part of the Navigation module, as they are both a form of navigation.

As all solutions develop and change we constantly need to consider if our module boundaries are correct and relevant for the customer’s domain.

Solution 2 – Introduce an Abstraction in the foundation layer

This is where you introduce an abstraction that satisfies the dependent modules requirements, in the foundation layer.

The External Profile Data challenge

A common challenge is when the profile/contact/customer/etc. data is stored in a 3rd party system i.e. Microsoft Dynamics CRM, Sales force, SQL database, Custom CRM, etc. For the blog post, we will assume it is stored in Microsoft Dynamics CRM.

Usually a Microsoft Dynamics CRM module is in the feature layer, and the solution will have several typical requirements:

  • Comments – Can only add comments if the user is logged in and you use their name from their profile data.
  • News – Can only see certain news articles if you are logged in.
  • XXX – Soon other modules, will need to know if the visitor is logged in and their name.

In general if a module depends on a 3rd party system they are very unstable, as you cannot control when their API will change. This is one reason that moving the code to the foundation layer is NOT a good idea.

Step 1 – Introduce abstractions

Introduce a new Profile module in the foundation layer, which contains the abstractions/interfaces that models the data/services for the Profile module.

It is much easier to model the data correctly and ensure it is relevant for the customer’s domain, when you do not have to concern yourself with the implementation details and or where the data is stored.

For this simple example, we will assume that the IProfileRepository returns NULL if the visitor is not logged in and the only data we need to retrieve or store is the visitor’s name. For the sake of simplicity, I have ignored how they are authenticated, and logged on.

namespace Foundation.Profile
{
interface IProfile
{
string Name { get; set; }
}
interface IProfileRepository
{
IProfile Get(); // returns null, if the visitor is not authenticated
void Update(IProfile profile);
}
}

Step 2 – Split into 2 feature modules

Whilst not strictly required, I would recommend splitting the Profile feature into 2 feature modules:

  1. Profile – which contains all the Profile UI i.e. Edit profile, Create profile etc.
  2. Customer Relations Management – This implements the abstractions declared in the Profile Foundation module and is responsible for integration with Microsoft dynamics CRM.

The reason I split into 2 modules is that the Profile module doesn’t need to know where its data is stored and probably does not require all the functionality exposed by Microsoft CRM, in addition it provides a number of benefits:

  1. Flexibility
    • This makes it easier to change where the data is stored/retrieved i.e. if you want to change CRM.
    • In my experience, a lot of enterprise customers store profile data initially in Sitecore, and then in later phases change to use their CRM.
    • Even if you use Sitecore, I would consider abstracting where contact/profile data is stored, as Sitecore’s API regarding this has a tendency to change.
  2. Multi-site support
    • In a multi-site solution likely that not all sites want to/can use Microsoft CRM; for example some sites might want to store it in xDB and or somewhere else.
    • It is possible to inject different implementations depending on the site context.
  3. Support multi versions
    • Microsoft Dynamics CRM will change their API and therefore in the transition period it will be possible to inject different version (i.e. a newer version if there is a specific query string, in a specific environment, etc).
  4. Simplifies development
    • The Profile feature is no longer concerned with the complexities of Microsoft CRM.
    • It is possible to mock out Microsoft CRM, so the profile module can be developed before the Microsoft CRM is implemented and or available.
    • The Microsoft CRM Integration does not have to be concerned with the Profile functionality and UI.
  5. Testing
    • Makes it possible to mock out Microsoft CRM and enable the testing of the other modules without Ax.
    • Enable side by side testing – call the same page twice, but inject in different versions of the implementation and ensure that they return the same result.
  6. CRM functionality
    • It is possible that other modules may require additional CRM functionality that is not relevant for the Profile module.

Step 3 – Change the feature modules to depend on the abstractions.

Change the feature modules (Comment, News, Profile, etc.) that require the profile data, to reference the profile interfaces in the foundation layer, and rely on dependency injection to resolve the concert implementation.

Step 4 – Setup Dependency Injection

The context project is responsible for injecting/registering the concrete implementation for the IProfileRepository and IProfile abstractions declared in the foundation layer.

In theory as there is currently only one implementation the Microsoft CRM module could be responsible for injecting/registering its own implementation, but like Page Types is responsible for aggregating what is shown on a page, the Context module is responsible for setting up the context for a given request.

Solution 3 – Move the module to the Foundation Layer

This solution is usually what people choose, but it is rarely a good idea! If the feature was abstract and or very stable, why was it not initially placed in the foundation layer?

So before doing this please consider, if the module is truly a foundation layer module or not and ensure the following

  1. Ensure you should not introduce an abstraction instead (see solution 1)
  2. Ensure that the module does not integrate with external systems (CRM, ERP, PIM, etc.) as by their nature they are very unstable, as you cannot control when their API will change, and therefore unsuitable for the foundation layer.
  3. Ensure as many classes as possible are declared as internal, to keep the public interface to a minimum.
  4. Ideally introduce interfaces and expose those instead of the concrete class implementations.

Solution 4 – Each module defines an interface and then a introduce a class in the project layer to to mediate.

This solution allows each feature to declare an interface to declare what it requires and then the project layer is responsible for routing the request to the relevant module. This solution is typically used to resolve the One module dependent on 2 or more modules (see part 2).

Hope this helped, please continue to part 2.