|
| 1 | +# ADR 1: Create a dedicated gem for conditional GraphQL content loading |
| 2 | +Status: Accepted |
| 3 | +Date: 2026-02-17 |
| 4 | + |
| 5 | +## Context |
| 6 | +As part of the migration to GraphQL-backed content retrieval [RFC-172](https://github.com/alphagov/govuk-rfcs/blob/main/rfc-172-graphql-for-govuk.md), we are introducing a temporary (medium-term) ConditionalContentItemLoader that determines whether a request should load content from: |
| 7 | +* The Content Store (REST), or |
| 8 | +* The Publishing API via GraphQL |
| 9 | + |
| 10 | +This decision is made at request time and depends on: |
| 11 | +* Request parameters |
| 12 | +* Environment state (e.g. draft host) |
| 13 | +* Allow-list configuration |
| 14 | +* Probabilistic traffic splitting |
| 15 | + |
| 16 | +It includes Prometheus metrics labelling for monitoring and alerting. |
| 17 | + |
| 18 | +The loader is required across seven frontend applications. |
| 19 | + |
| 20 | +## Problem |
| 21 | +We need a centralised implementation to: |
| 22 | +* Avoid duplication and divergence across multiple apps |
| 23 | +* Maintain clear architectural boundaries |
| 24 | +* Keep configuration and routing logic co-located |
| 25 | + |
| 26 | +## Decision |
| 27 | +We will create a Ruby gem to encapsulate the conditional content loader and its configuration, with minimal dependencies. |
| 28 | + |
| 29 | +## Rationale |
| 30 | +1. Shared across multiple applications |
| 31 | +One gem ensures consistent behaviour and easier rollout coordination. |
| 32 | + |
| 33 | +1. Maintenance burden is acceptable |
| 34 | +The gem is small, with few dependencies. |
| 35 | + |
| 36 | +1. Not a good fit for `gds-api-adapters` |
| 37 | +`gds-api-adapters` is designed around a clear architectural pattern: |
| 38 | +* Each adapter corresponds to a single external API |
| 39 | +* Adapter methods map directly to HTTP endpoints |
| 40 | +* Namespaces reflect a 1:1 relationship with APIs |
| 41 | + |
| 42 | +The conditional loader does not fit this model as it's a higher-level orchestration layer, not a client abstraction. Adding it to gds-api-adapters would: |
| 43 | +* Blur architectural boundaries |
| 44 | +* Violate separation of concerns |
| 45 | +* Break the established software design pattern of the gem |
| 46 | + |
| 47 | +1. Not a good fit for `govuk_app_config` |
| 48 | +That library is intended for generic application configuration, not behaviour orchestration used by a subset of apps. |
| 49 | + |
| 50 | +1. Explicitly not using `govuk_ab_testing` |
| 51 | +`govuk_ab_testing` was considered. This is not a generic A/B test or experiment framework use case. Embedding this logic in govuk_ab_testing would conflate experimentation infrastructure with migration routing logic. |
| 52 | + |
| 53 | +1. Co-location with configuration |
| 54 | +The loader depends on configuration established via GovukGraphqlTrafficRates.configure which would fit in govuk_app_config. However, keeping it in the same gem reduces complexity and risk of misconfiguration. |
| 55 | + |
| 56 | +## Alternatives considered |
| 57 | +- Duplicate in each app – rejected due to divergence risk. |
| 58 | +- Add to `gds-api-adapters` – rejected; violates separation of concerns. |
| 59 | +- Add to `govuk_app_config` – rejected; not just application configuration. |
| 60 | +- Add to `govuk_ab_testing` – rejected; not experimentation logic. |
| 61 | + |
| 62 | +## Consequences |
| 63 | + |
| 64 | +### Positive |
| 65 | +* Consistent behaviour across multiple applications |
| 66 | +* Clean architectural separation |
| 67 | +* Centralised rollout |
| 68 | +* Clear ownership of GraphQL migration logic |
| 69 | +* Versioned upgrades |
| 70 | + |
| 71 | +### Negative |
| 72 | +* One additional gem to maintain |
| 73 | +* Increase in dependency management overhead |
0 commit comments