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

sona11
What is the difference between tuple relational calculus (TRC) and domain relational calculus (DRC)? What distinguishes them from relatio...
New
harwind
First have a look at the code: function mainfunc(func, par3, par2){ window[func](par3, par2); } function calledfunc(par3, par2){ ...
/js
New
Fl4m3Ph03n1x
Background I have a fresh umbrella app and I am trying to create a Phoenix app inside it. However, even though I can create the Phoenix a...
New
Fl4m3Ph03n1x
Background I have created a fresh Phoenix app using mix phx.new.web web_interface --no-dashboard --no-ecto --no-gettext --no-mailer insid...
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
pjamesrud
I am creating an app that allows user to enter values and depending on the value a number of textviews are changed programmatically from ...
New
PickyBiker
I have done small amounts of programming for IOS and for Android, but now I need to create something that works with both. What are the ...
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
SteelFork2819
hi does anyone know how to render a cloud-stored 3D file and move a camera around in it using native html5 functions? i really don’t know...
New
selamibaba
I maintain a content website built around a fixed domain of 78 unique items. This is the current overview page showing the complete data...
New

Other popular topics Top

PragmaticBookshelf
Free and open source software is the default choice for the technologies that run our world, and it’s built and maintained by people like...
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
Help
I am trying to crate a game for the Nintendo switch, I wanted to use Java as I am comfortable with that programming language. Can you use...
New
PragmaticBookshelf
Author Spotlight Mike Riley @mriley This month, we turn the spotlight on Mike Riley, author of Portable Python Projects. Mike’s book ...
New
PragmaticBookshelf
Author Spotlight Rebecca Skinner @RebeccaSkinner Welcome to our latest author spotlight, where we sit down with Rebecca Skinner, auth...
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
sir.laksmana_wenk
I’m able to do the “artistic” part of game-development; character designing/modeling, music, environment modeling, etc. However, I don’t...
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
AstonJ
This is cool! DEEPSEEK-V3 ON M4 MAC: BLAZING FAST INFERENCE ON APPLE SILICON We just witnessed something incredible: the largest open-s...
New
Fl4m3Ph03n1x
Background Lately I am in a quest to find a good quality TTS ai generation tool to run locally in order to create audio for some videos I...
New