Introduction
We recently worked on an Angular-based project where the front-end team was developing in a micro-front-end architecture—completely isolated from the back-end and with no exposure to Optimizely CMS. Their world revolved around Angular development and independently deployed micro-front-end applications.
Our challenge was to introduce Optimizely as the CMS backbone for content authoring and to manage the content tree structure. We needed a foundational architecture that allowed both front-end and back-end teams to work productively and in parallel across sprints—without stepping on each other’s toes.

The Challenge
The biggest challenge was alignment. The front-end and back-end teams needed clarity on:
- How data would flow from Optimizely CMS to the UI
- How the front-end could leverage Optimizely Content Graph (GraphQL) without taking on the complexity of schema management or query optimization
- How both teams could iterate without waiting on each other
We needed to empower the front-end team to build components without having to learn or directly interface with Optimizely.
The Solution
We implemented a Back-end for Front-end (BFF) pattern, creating a custom Layout Service that acts as a middle layer between Optimizely and the front-end. This layout service:
- Executes and manages GraphQL queries
- Aggregates and transforms content from the CMS
- Delivers clean JSON structured data for the front-end to consume
On top of this, we developed an Angular library that binds this back-end data to UI components using a component mapping strategy. To top it off, we also provided a sample Angular application to demonstrate the architecture and serve as a blueprint for the front-end team.
The Problem We Faced
One of our early challenges was onboarding the Angular front-end team, who came from a different vendor and had no prior experience with Optimizely CMS, Content Graph, or GraphQL. Understandably, expecting them to navigate the GraphQL schema and align their components with CMS content was overwhelming.
To ease the learning curve, we started by showing them a basic implementation approach—simple component structures and static data examples. Once that foundation was set, we took the complexity off their plate by moving all GraphQL handling to the back-end, encapsulating it inside the Layout Service. This decision significantly reduced the front-end overhead and let the Angular team focus purely on UI development.
Another challenge: once the front-end team became dependent on API responses from our layout service, we adjusted our approach so the back-end team worked one sprint ahead to stay aligned. This allowed us to build content blocks, APIs, and models just in time for the front-end to pick them up without delays.
And lastly, like many headless systems, Optimizely Content Graph returns content in a structured, standard format—great for consistency, but difficult to work with unless you’re already familiar with how Optimizely models content. Without a translation layer like the Layout Service, the front-end effort would have slowed down considerably. The Layout Service became that bridge.
Designing the Architecture
There were two architectural options:
- Direct Access to Optimizely Content Graph
- Encapsulation via BFF and Layout Service (chosen)
We went with option 2.
The reasons were clear:
- Decoupling and Separation of Concerns
- Front-end Angular developers don’t need to understand Optimizely schema, GraphQL syntax, or CMS internals.
- We could standardize request/response formats, minimizing payload size and simplifying integration
- The BFF approach offered flexibility for future changes in schema or query logic, without touching the UI code
- Back-end owns the logic of content querying, shaping, filtering, and formatting.
- Easier to onboard new developers on either side without cross-training.
- Faster Front-end Development
- Front-end receives clean, predictable, flat JSON payloads optimized for rendering.
- No need to traverse nested GraphQL structures or handle response normalization.
- Reduces dev time and boilerplate code in Angular or any consuming app.
- Better Security
- Prevents direct exposure of GraphQL endpoints to the client.
- The Graph App Key and Secret (with write access) are kept server-side—never exposed in the client bundle, avoiding risk in case of leaks.
- Authentication/authorization logic can be centrally managed in the BFF layer.
- Caching & Payload Optimization
- Smaller, faster payloads: The BFF trims and shapes GraphQL responses for optimal front-end consumption.
- The layout service can cache GraphQL responses or transformed layouts, reducing load on the Optimizely backend and speeding up delivery.
- Inject business logic or transformations into the content pipeline before reaching the UI.
- Centralized Query Optimization
- Master queries can be tuned for performance without affecting front-end code.
- Efficient query batching and caching at the layout service level.
- Enables reuse of queries and dynamic building of complex content trees.
- Improved Testing and Debugging
- Easy to test the layout service independently with mock CMS data.
- Central place to log and trace requests/responses for diagnostics.
- Supports Future Front-end Framework Changes
- Front-end can switch from Angular to React, Vue, etc. without touching the Optimizely query logic.
Creating the “master query” for Optimizely PaaS did take some effort—but once we had it, everything clicked into place.
Introducing the Layout Service
The Layout Service became the heart of our architecture. Here’s what it does:
- Constructs and executes GraphQL master queries to fetch content
- Maps and transforms content when needed (handling exceptions and layout customization)
- Delivers structured JSON output to the front-end
On the other side, the Angular library (built by our Gravitas team) uses a component key-based mapping. This way:
- The layout service sends content blocks with a component identifier
- The Angular library matches each component key with a visual component
- Content is dynamically bound with design using simple, reusable templates
This let the front-end team build fast, without worrying about CMS internals.
Workflow Improvements
This architecture enabled true parallel development:
- Back-end teams created content blocks, components, and data models
- Front-end teams used the layout service and Angular library to develop UI components independently
Benefits:
- Faster sprints with less cross-team dependency
- Easier unit testing and prototyping
- Reusable components for future pages and experiences
Lessons Learned
- Invest in the foundation early. Defining the architecture up front saved us from major rework. Getting structured feedback early made a big difference.
- Initial alignment was tough. There were no boilerplate patterns or starter kits from Optimizely for Angular MFE. We had to prototype and build from scratch.
- The sample Angular app helped a lot. It clarified the data flow, interaction model, and made it easier for both teams to speak the same language.
Closing Thoughts
At Gravitas, we strive to build CMS solutions not just for content authors, but also to empower delivery teams—back-end, front-end, DevOps alike. This headless architecture, based on Optimizely’s Content Graph and layered with a BFF approach, gave us speed, structure, and flexibility.
We were also fortunate to align our solution with Optimizely’s own senior team, whose support was invaluable throughout the project.
I’d love to hear how others in the community are tackling similar challenges. Have you used BFF or layout services with Optimizely? What’s worked for you? Let’s share and learn together.