https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2018/8/mvc-feature-folders-and-episerver--net-core-and-episerver/
Hi! I want to start by saying that you should prepare for a significant amount of work if you want to accomplish this. Since the sites share the same DB, all content types from all projects will of course be synchronized. You will have to create custom implementations for a few services to be able to use different pages/blocks per site, one of them is ContentTypeAvailabilityService. If you need any shared pages/blocks you might have to create some feature switching between the sites, and a ViewEngine that prioritize views from the executing site etc.
I have personally implemented a multi site solution successfully which prioritizes controllers and views from within a site folder by matching folder name with executing site name. I used one codebase for this though. This was quite complex and took me a while to achieve, and it involved creating a custom ViewEngine, ITemplateResolver, ContentTypeAvailabilityService etc. The documentation was quite poor at that time so I had to do a lot of digging and trial and error.
Here is one link, quite old, but should still be relevant and of help if you need to create a custom ContentTypeAvailabilityService:
I'm involved in a similar undertaking right now. Some of the strategies we're using include:
For a new CMS 12 solution we are going to separate code by using Razor Class Libraries (RCL) (how you should do add-ons now as well) and have a layered structure. In razor class libs views and static files are out-of-the-box compiled as well (with some caveats regarding location of views).
Our website project only has add-on packages, appsettings and the startup.cs logic and references to the rest.
We have a foundation RCL project that is shared code (alloy/foundation infrastructure like functionality, base models etc.)
Sites or/and feature are then spiit into separate feature RCL
Then we also have planned some core libraries (Clean/Onion Architecture) that have no deps to episerver etc.
It looks promising at this point :)
I am guessing it will only work for CMS12+ and will of course require some refactoring if you have a solution already.
To add to this, this template selection approach might also be useful for start pages in a multi-site solution:
https://www.getadigital.com/blog/let-the-editor-choose-a-template-for-content
This could also be slightly modified so a site specific template is automatically selected based on current site context, maybe something like this:
[AttributeUsage(AttributeTargets.Class)]
public class TemplateForSitesAttribute : Attribute
{
public TemplateForSitesAttribute(params string[] siteNames)
{
SiteNames = siteNames;
}
public readonly string[] SiteNames { get; }
}
[TemplateDescriptor(Name = "My first start page template", Default = true)]
[TemplateForSites("SiteName1", "SiteName2")]
public class StartController : PageController<StartPage>
{
}
[ContentType(DisplayName = "Start page")]
public class StartPage : PageData, IDynamicTemplateContent
{
public void SetDynamicTemplate(TemplateResolverEventArgs args)
{
if (SelectedTemplateIsPreviewController(args.SelectedTemplate))
{
return;
}
TemplateModel selectedTemplate = args
.SupportedTemplates
.FirstOrDefault(tmpl => IsForCurrentSite(tmpl));
if (selectedTemplate != null)
{
// A template targeting current site was found. Switch to it.
args.SelectedTemplate = selectedTemplate;
}
}
private bool SelectedTemplateIsPreviewController(TemplateModel selectedTemplate)
{
if (selectedTemplate == null || selectedTemplate.Tags == null)
{
return false;
}
return Array.IndexOf(selectedTemplate.Tags, RenderingTags.Preview) > -1;
}
private bool IsForCurrentSite(TemplateModel templateModel)
{
var templateForSites = templateModel.TemplateType.GetCustomAttribute<TemplateForSitesAttribute>();
if (templateForSites != null)
{
return templateForSites.SiteNames.Contains(SiteDefinition.Current.Name);
}
return false;
}
}
You could of course also use interfaces to decorate your controllers to be able to select the correct one for executing site.
Lot of good ideas above. Just wanted to share a few more blog posts talking about different approaches:
Currently, our single-project Episerver solution serves up a single website and has been working great. Soon we'll be developing an additional, separate Episerver site that will need to live within the same solution but I would love for the two codebases to remain separate while still sharing the same Episerver DB. For the site that currently exists, we have a lot of page/block models, so I expect any refactoring will be no light lift.
Is there any documentation/guidance, aside from this, that details out best practices on how to separate out the models, controllers, views, etc for each site into their own project?
Is this better achieved through the use of Areas instead of separate projects? Some other approach I'm not considering?
Any help and/or discussion around this is greatly appreciated.