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:
- Introduce an abstraction and implemented the abstraction in the project layer.
- Introduce an Abstractions in the foundation layer.
- 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:
- If it is a news article, use the news title.
- If it is a blog post, use the blog post.
- If it is a product page, get the product title from the database.
- 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.