Skip to main content

Outline

At a glance
  • Core principle: Keep CMS-specific logic separate from business logic.
  • Architectural goal: Improve maintainability, testability, and scalability.
  • Practical method: Use layered architecture, DI, and service abstractions.
  • DXP advantage: Easier upgrades and headless flexibility.

This module explores the critical principle of Separation of Concerns (SoC) within the context of Optimizely CMS 12 and Commerce 14 development. While Optimizely provides a powerful platform for content and commerce, it is essential to distinguish between responsibilities that are inherently CMS-specific and those that represent general .NET application logic.

The Importance of Separation of Concerns

Separation of Concerns advocates dividing software into distinct sections where each section handles a specific responsibility.

Core Benefits

  • Maintainability: Changes are isolated and easier to manage.
  • Testability: Components can be tested independently.
  • Scalability: Independent layers can scale separately.
  • Reusability: Clean components can be reused across projects.
  • Easier Upgrades: Platform changes have minimal impact on core logic.

Defining CMS-Specific Concerns

1. Content Definition and Structure

  • Content Types: Page, block, media, and catalog definitions.
  • Content Models: Classes inheriting from PageData, BlockData, CatalogContentBase.
  • Property Editors: Custom UI editors for content fields.

2. Content Lifecycle

  • Loading: Using IContentLoader.
  • Saving/Publishing: Using IContentRepository.
  • Events: Handling ContentEvents.

3. UI & Routing

  • Rendering: Controllers and View Components tied to IContent.
  • Edit Mode Enhancements: Custom CMS UI extensions.
  • Optimizely Routing: URL resolution logic.

Defining General .NET Responsibilities

1. Core Business Logic

  • Pricing rules, fulfillment workflows, recommendation engines.
  • Logic independent of how content is stored or rendered.

2. External Data & Integrations

  • External databases not managed by Optimizely.
  • Payment gateways, CRM, ERP integrations.

3. Cross-Cutting Concerns

  • Logging: Serilog or Microsoft.Extensions.Logging.
  • Error Handling: Global exception middleware.
  • Security: Identity policies and authorization logic.
  • Configuration: General app settings.

Strategies for Achieving Separation

1. Layered Architecture

  • Presentation Layer: Controllers, Views, View Components.
  • Application Layer: Orchestrates business workflows.
  • Domain Layer: Core business rules (no Optimizely dependency).
  • Infrastructure Layer: External services, repositories.

2. Custom Service Abstractions

Example: Business Interface vs Optimizely Implementation

// Core/Domain project (no Optimizely dependency) public interface IProductService { Product GetProductDetails(string sku); IEnumerable<Product> GetFeaturedProducts(); } // Optimizely implementation public class OptimizelyProductService : IProductService { private readonly IContentLoader _contentLoader; private readonly IProductCatalog _productCatalog; public OptimizelyProductService( IContentLoader contentLoader, IProductCatalog productCatalog) { _contentLoader = contentLoader; _productCatalog = productCatalog; } public Product GetProductDetails(string sku) { return _productCatalog.GetProduct(sku); } }

3. Separate Projects

  • MySolution.Core – Domain models & interfaces
  • MySolution.Application – Business logic
  • MySolution.Infrastructure – External integrations
  • MySolution.Web – Optimizely CMS/Commerce application

Benefits in an Optimizely DXP Context

  • Easier Upgrades: Less coupling to platform internals.
  • Improved Testability: No CMS runtime required for business tests.
  • Headless Flexibility: Domain layer can serve multiple front-ends.
  • Reduced Complexity: Clear boundaries improve onboarding.

Conclusion

Separating CMS concerns from general .NET responsibilities is foundational for building maintainable and scalable Optimizely CMS 12 and Commerce 14 solutions. By applying layered architecture, Dependency Injection, and clear service abstractions, teams ensure that core business logic remains platform-independent—future-proofing their digital solutions.