selamibaba
How would you model a fixed 78-item content domain without duplicating data?
I maintain a content website built around a fixed domain of 78 unique items.
This is the current overview page showing the complete data set:
Disclosure: I maintain the linked website. I am sharing it only to show the real content structure that I am trying to model.
The domain consists of:
-
22 Major Arcana cards
-
56 Minor Arcana cards
-
Four suits
-
Fourteen ranks within each suit
-
Upright and reversed interpretations
-
Stable URL slugs
-
Related meanings for love, career, feelings and yes-or-no questions
-
Individual editorial pages for each card
-
Interactive tools that also need access to the same card data
At the moment, some information exists in CMS entries, some in template code and some in JavaScript used by interactive tools.
I would like to move toward one reliable source of truth without sending the complete editorial content bundle to every browser.
A simplified TypeScript model might look like this:
type Suit = "wands" | "cups" | "swords" | "pentacles";
type Rank =
| "ace"
| "two"
| "three"
| "four"
| "five"
| "six"
| "seven"
| "eight"
| "nine"
| "ten"
| "page"
| "knight"
| "queen"
| "king";
type Orientation = "upright" | "reversed";
type MajorCard = {
kind: "major";
id: string;
number: number;
slug: string;
title: string;
};
type MinorCard = {
kind: "minor";
id: string;
suit: Suit;
rank: Rank;
slug: string;
title: string;
};
type Card = MajorCard | MinorCard;
The longer editorial content could be stored separately:
type CardContent = {
cardId: string;
uprightMeaning: string;
reversedMeaning: string;
loveMeaning?: string;
careerMeaning?: string;
feelingsMeaning?: string;
yesOrNo?: "yes" | "no" | "maybe";
updatedAt: string;
};
I am considering four architectures.
Option 1: CMS as the source of truth
All metadata and editorial content remain in the CMS.
The frontend retrieves the entries through an API during the build or at request time.
Advantages:
-
Editors can work without touching the repository
-
Content publishing remains straightforward
-
No separate synchronization process
Concerns:
-
Invalid or incomplete records may be published
-
Relationships between cards may be difficult to validate
-
Build output depends on an external service
-
Interactive tools may require a second representation of the same data
Option 2: Versioned JSON or YAML
The complete card manifest is stored in the repository and validated during CI.
Long editorial content remains in the CMS.
Advantages:
-
Stable IDs, slugs and structural data are version controlled
-
The application can generate pages, routes and tests from the manifest
-
Breaking schema changes are visible in pull requests
Concerns:
-
Editors may need developer assistance when structural metadata changes
-
CMS entries and the repository manifest can drift apart
Option 3: TypeScript package
Create a small internal package containing card types, IDs, slugs and utility functions.
The website, calculators and editorial tools would all consume the same package.
Advantages:
-
Strong typing across applications
-
Reusable parsing and validation
-
Exhaustive handling of the fixed card set
Concerns:
-
Editorial content should probably not be bundled into the package
-
Updating one item may require publishing a new package version
Option 4: Database with generated types
Store everything in a relational database and generate TypeScript types from the schema.
Advantages:
-
One queryable source
-
Better support for editorial workflows and reporting
-
Easier localization and content versioning
Concerns:
-
More infrastructure than a relatively small fixed domain may require
-
Static page generation becomes dependent on database availability
My main questions are:
1. Where would you place the canonical identity data?
Would you consider the list of 78 IDs and slugs application code, CMS content or database data?
The cards themselves are fixed, but their descriptions change over time.
2. How would you validate completeness?
I want CI to verify that:
-
There are exactly 78 standard cards
-
Every ID and slug is unique
-
Every Minor Arcana suit contains all fourteen ranks
-
Every card has upright and reversed content
-
Every card has exactly one canonical page
-
Every related-card reference points to a valid card
Would you use JSON Schema, Zod, custom TypeScript assertions or database constraints?
3. How should stable identifiers differ from slugs?
Slugs may change for SEO or editorial reasons, but internal references should remain stable.
Would you use opaque IDs such as:
major-21
minor-wands-07
while treating the public slug as mutable presentation data?
4. Build-time or runtime content retrieval?
Most pages are public editorial pages and change infrequently.
Would you generate them statically and rebuild when content changes, or use server-side rendering with caching and revalidation?
5. How would you avoid shipping all content to the client?
Interactive tools need card IDs, titles, images and short summaries, but they do not need every full-length article.
Would you maintain separate public and editorial projections of the same data, or generate a lightweight client manifest automatically?
6. Localization
A translated card should retain the same internal ID but have a different title, slug and article content.
Would you model localized content as:
type LocalizedCardContent = {
cardId: string;
locale: string;
slug: string;
title: string;
content: CardContent;
};
Or keep routes and translations in a separate localization layer?
7. Generated types or handwritten unions?
Because the domain is fixed, an exhaustive union provides useful compiler guarantees.
However, manually writing and maintaining 78 constructors or literal values feels repetitive.
Would you generate TypeScript types from the validated manifest, or treat the manifest itself as the runtime source and derive the types using as const?
8. CMS synchronization
If structural data lives in the repository but editorial content lives in the CMS, how would you prevent drift?
I am considering a CI job that fetches the CMS records and validates them against the versioned card manifest before deployment.
Is that a reasonable boundary, or is splitting ownership between two systems likely to cause more problems than it solves?
My current preference is:
-
Versioned manifest for stable IDs, structure and relationships
-
CMS for long editorial content
-
Schema validation during CI
-
Generated lightweight JSON for interactive tools
-
Static generation with targeted revalidation after publishing
I would appreciate feedback from anyone who has built a content-heavy site around a small but strictly defined domain.
Popular Frontend topics
Other popular topics
Categories:
Sub Categories:
Popular Portals
- /elixir
- /rust
- /wasm
- /ruby
- /erlang
- /phoenix
- /keyboards
- /python
- /js
- /rails
- /security
- /go
- /swift
- /vim
- /java
- /clojure
- /emacs
- /haskell
- /typescript
- /svelte
- /onivim
- /kotlin
- /c-plus-plus
- /crystal
- /tailwind
- /react
- /gleam
- /ocaml
- /elm
- /flutter
- /vscode
- /html
- /ash
- /deepseek
- /zig
- /opensuse
- /centos
- /php
- /scala
- /react-native
- /lisp
- /textmate
- /sublime-text
- /nixos
- /debian
- /agda
- /deno
- /django
- /kubuntu
- /arch-linux
- /nodejs
- /spring
- /ubuntu
- /revery
- /manjaro
- /julia
- /diversity
- /lua
- /quarkus
- /markdown









