🌐 Website Implementation
Technical Architecture Components
📖 Docs

Technical Architecture & Components - Optimizely CMS 12

Exam Area: Content Area 3 – Website Implementation & Delivery (25%)
Reference: https://docs.developers.optimizely.com/content-management-system/docs/architecture


1. Architecture Overview

Layers:

┌──────────────────────────────────────────────────┐
│  Presentation Layer                               │
│  (MVC Controllers, Razor Views, React)           │
├──────────────────────────────────────────────────┤
│  Business Logic Layer                            │
│  (Services, ViewModels, Helpers)                 │
├──────────────────────────────────────────────────┤
│  Content API Layer                               │
│  (IContentRepository, IContentLoader)            │
├──────────────────────────────────────────────────┤
│  Framework Layer                                 │
│  (DI, Events, Caching, Globalization)            │
├──────────────────────────────────────────────────┤
│  Data Layer                                      │
│  (SQL Server, Blob Storage)                      │
└──────────────────────────────────────────────────┘

2. MVC Architecture

Page Controller Pattern:

// Controller inherits from PageController<T>
public class ArticlePageController : PageController<ArticlePage>
{
    private readonly IContentRepository _repo;

    public ArticlePageController(IContentRepository repo)
    {
        _repo = repo;
    }

    public ActionResult Index(ArticlePage currentPage)
    {
        var model = new ArticleViewModel(currentPage);
        return View(model);
    }
}

Routing:


3. Template Resolution

// Register template for a content type
[TemplateDescriptor(
    Inherited = true,
    TemplateTypeCategory = TemplateTypeCategories.MvcController)]
public class ArticlePageController : PageController<ArticlePage> { }

// Multiple templates for the same type
[TemplateDescriptor(
    Tags = new[] { "Wide", "Narrow" },
    TemplateTypeCategory = TemplateTypeCategories.MvcPartialController)]
public class TeaserBlockNarrowController : BlockController<TeaserBlock> { }

4. ViewModels

// ViewModel pattern
public class ArticleViewModel : ContentViewModel<ArticlePage>
{
    public ArticleViewModel(ArticlePage currentPage) : base(currentPage) { }
    
    public string FormattedDate => CurrentPage.PublishDate?.ToString("dd MMM yyyy");
    public IEnumerable<ArticlePage> RelatedArticles { get; set; }
}

// In the Controller
public ActionResult Index(ArticlePage currentPage)
{
    var model = new ArticleViewModel(currentPage)
    {
        RelatedArticles = GetRelatedArticles(currentPage)
    };
    return View(model);
}

5. Content Models - Project Structure

Models/
├── Pages/
│   ├── StartPage.cs
│   ├── ArticlePage.cs
│   └── ProductPage.cs
├── Blocks/
│   ├── TeaserBlock.cs
│   ├── HeroBlock.cs
│   └── AccordionBlock.cs
├── Media/
│   ├── ImageFile.cs
│   └── VideoFile.cs
└── ViewModels/
    ├── ArticleViewModel.cs
    └── SearchViewModel.cs

Views/
├── ArticlePage/
│   └── Index.cshtml
├── Blocks/
│   ├── TeaserBlock.cshtml
│   └── HeroBlock.cshtml
└── Shared/
    └── _Layout.cshtml

6. Content Rendering Pipeline

HTTP Request
    │
    ▼
CMS Routing (ContentRoute)
    │
    ▼
Template Resolver → Selects Controller/View
    │
    ▼
Controller.Index(currentPage)
    │
    ▼
View Model Created
    │
    ▼
View Rendered (Razor)
    │  ├── @Html.PropertyFor() → On-page editing enabled
    │  └── @Html.FullRefreshPropertyFor() → Full refresh
    ▼
Response (HTML)

7. Partial Views for Blocks

// Block controller
public class TeaserBlockController : BlockController<TeaserBlock>
{
    public override ActionResult Index(TeaserBlock currentBlock)
    {
        return PartialView(currentBlock);
    }
}
@* Views/Blocks/TeaserBlock.cshtml *@
@model TeaserBlock

<div class="teaser">
    <h3>@Html.PropertyFor(m => m.Heading)</h3>
    @Html.PropertyFor(m => m.Body)
</div>

8. Apps and Integrations

Common integrations:

// Register integration
builder.Services.AddOptimizelyDataPlatform(options =>
{
    options.ProjectId = "your-project-id";
});

9. IOC Container Deep Dive

// ServiceConfigurationContext in IConfigurableModule
public void ConfigureContainer(ServiceConfigurationContext context)
{
    // Get IServiceCollection
    var services = context.Services;
    
    // Register
    services.AddSingleton<IMyService, MyService>();
    
    // Override
    services.TryAddSingleton<ICacheService, RedisCacheService>();
    
    // Decorator
    services.Decorate<IContentLoader, LoggingContentLoader>();
    
    // Named registrations
    services.AddTransient<ISearchProvider, GoogleSearchProvider>(
        serviceProvider => new GoogleSearchProvider("api-key"));
}

10. Integration Methods

REST API Integration:

// Optimizely Content Delivery API (headless)
builder.Services.AddContentDeliveryApi();

Webhook Integration:

// CMS events → Webhook
[InitializableModule]
public class WebhookModule : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        var events = context.Locate.Advanced.GetInstance<IContentEvents>();
        events.PublishedContent += async (sender, e) =>
        {
            await NotifyWebhook(e.ContentLink);
        };
    }
}

Review Questions

  1. What does PageController<T> inherit from? (Controller in ASP.NET Core MVC)
  2. What is app.MapContent() used for? (Registers CMS content routing)
  3. Which attribute is used for Template Resolution? ([TemplateDescriptor])
  4. What does a Block Controller inherit from? (BlockController<T>)
  5. What should ViewModels inherit from? (ContentViewModel<T>)