In my attempt to follow the Helix design principles as closely as possible, the issue of Feature-to-Feature module references has come up quite a bit and still remains a bit fuzzy to me.
According to the Helix Documentation, these types of references appear to be permitted in some cases, but are generally discoraged.
A templates class should never define constants for templates that are not created in the module itself. If a module needs to reference a template or field in another module, it should reference the Templates struct in that module ...
The practice of referencing different fields across modules by their shared name – an equivalence to duck typing – is discouraged ... If modules need to share data, consider using design patterns such as providers or pipelines to allow one feature to inject content into another feature.
Foundation Providers/Pipelines
The examples of using providers or piplines are pretty limited. The only real example I could find in Habitat was in Feature.Search
, which calls upon SearchServiceRepository
from Foundation.Indexing
, which in turn determines what fields are included in the serach as well as how to format results based on IndexContentProviderBase
implementations within each feature.
This is a rather complex approach that is probably overkill for something as simple as a rendering breadcrumbs for wildcard items or when fields from other Features are required, and this probably explains why Habitat has a //TODO
for falling back to Title / DisplayName fields in Feature.Metadata
.
Following the Foundation.Indexing
example in Habitat, you would register a provider or pipeline, from the feature layer, that returns a piece of formatted text when certain conditions are met. For example, this provider/pipeline could have an IsValid()
method that determines that it should be used when the context item is a wildcard item (ex: Context.Item.Name == "*"
) or when the context item is derived from a Blog template.
Since it would be possible for multiple providers/pipelines to be considered valid, I perfer using pipeliens over providers, because you can control the order of how pipelines are executed more easily, and they lend themselves combining values from multiple features. Lastly, the Foundation module would be responsible for running the pipeline (or determining the correct provider), and it can be called from your Breadcrumb feature.
"Project" Features
The idea here is to move your Breadcrumb rendering and related business logic to the Project layer, thus making it a pseudo-feature that is project specific. In the project layer, it can safely make a reference to any Features it needs (in your case, Feature.Blog
). Some may feel that this goes against the definitons of a Project and Feature module, however, for simple logic such as Breadcrumbs, it may be acceptable.
Screw it, just make the reference
At the end of the day, we are talking about a single reference, so you may determine that re-architecting your solution to avoid referencing a single field from another feature may be overkill. Hopefully there will be more examples around this issue and clearer definitions in the future so that these references can be avoided.