selamibaba

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:

  1. Versioned manifest for stable IDs, structure and relationships

  2. CMS for long editorial content

  3. Schema validation during CI

  4. Generated lightweight JSON for interactive tools

  5. 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.

Where Next?

Popular Frontend topics Top

New
AlessandroDsgroup
Hi to everyone, we are experiencing a 401 error related to the connection of the websocket (in reference to our web app); we are unable ...
New
harwind
First have a look at the code: function mainfunc(func, par3, par2){ window[func](par3, par2); } function calledfunc(par3, par2){ ...
/js
New
WiseDan
hi everybody , am new in gsap.js so i wanted load content in my home page when user scrolling , but since am reading the documentation ...
New
Fl4m3Ph03n1x
Background I have a Phoenix application, where all pages (expect the login page) have a menu at the top. This menu will therefore only a...
New
Fl4m3Ph03n1x
Background I have Phoenix umbrella application. When inside said application, I can run it without issues if MIX_ENV=prod. However, if I ...
New
Julien0577
Hi all, Anybody knows how to do this menu animation? (from BBVA APP, they have the same for both android and iOS app). Is it custom?...
New
hosseinkhosromanesh
hello , i should code a cluster like image bellow we have no challenge in coding backend but in front need some clue to do this its a dy...
/js
New
Arpeggio
I have the following HTML structure, which is dynamically rendered from a Sightly (HTL) page in a new AEM component we’re building, so I ...
New
Benjamin-Philip
I’m curious about designing some websites and applications for personal (and potentially public via open sourcing) use. I’m an experience...
New

Other popular topics Top

Devtalk
Reading something? Working on something? Planning something? Changing jobs even!? If you’re up for sharing, please let us know what you’...
1063 24245 395
New
PragmaticBookshelf
Design and develop sophisticated 2D games that are as much fun to make as they are to play. From particle effects and pathfinding to soci...
New
AstonJ
In case anyone else is wondering why Ruby 3 doesn’t show when you do asdf list-all ruby :man_facepalming: do this first: asdf plugin-upd...
New
AstonJ
If you get Can't find emacs in your PATH when trying to install Doom Emacs on your Mac you… just… need to install Emacs first! :lol: bre...
New
DevotionGeo
I have always used antique keyboards like Cherry MX 1800 or Cherry MX 8100 and almost always have modified the switches in some way, like...
New
PragmaticBookshelf
Get the comprehensive, insider information you need for Rails 8 with the new edition of this award-winning classic. Sam Ruby @rubys ...
New
AnfaengerAlex
Hello, I’m a beginner in Android development and I’m facing an issue with my project setup. In my build.gradle.kts file, I have the foll...
New
PragmaticBookshelf
A masterclass in the fundamentals and principles of functional programming. Minh Quang Tran The Art of Functional Programming is a ma...
New
RobertRichards
Hair Salon Games for Girls Fun Girls Hair Saloon game is mainly developed for kids. This game allows users to select virtual avatars to ...
New
PragmaticBookshelf
Quickly turn complex problems into simple, working solutions using functional programming, safe concurrency, and the expressive tools of ...
New