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

davearonson
I’m unit-testing some JS, with Jasmine, and I’d like to check our coverage. We’re not using any front-end framework, nor much JS, so no ...
New
New
ClaudiaGiv
I have a sveltekit project that is using GoTrue library for authentication. In development mode (npm run dev) everything works but when I...
New
beberardinelli
Hi! I just started coding a few months ago and I am trying to get all the help I can get. My friend showed me this debugging tool called...
New
david-j-m
Hi, have a svelte spa gallery site - repl here. Have a couple of category buttons - Oil on Canvas and WaterColor… Sidebar contains all im...
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
JessicaW33
Hello everyone, I’m working on optimizing the performance of React Native applications and would like to gather insights and suggestions...
New
Fl4m3Ph03n1x
Background I have a custom component in my LiveView, which is basically a group of checkboxes. I want to add a new attribute to my custo...
New
avipal
I have an application where it is three layer 1st layer- A legacy core routines 2nd layer- built on top of the Core routine using Dotno...
New
ounce591
I am currently designing the navbar of a workout tracking app written using React Native. The navbar has three buttons: Splits/Plans ...
New

Other popular topics Top

AstonJ
Or looking forward to? :nerd_face:
503 15149 280
New
siddhant3030
I’m thinking of buying a monitor that I can rotate to use as a vertical monitor? Also, I want to know if someone is using it for program...
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
I ended up cancelling my Moonlander order as I think it’s just going to be a bit too bulky for me. I think the Planck and the Preonic (o...
New
New
New
First poster: AstonJ
Jan | Rethink the Computer. Jan turns your computer into an AI machine by running LLMs locally on your computer. It’s a privacy-focus, l...
New
NewsBot
Node.js v22.14.0 has been released. Link: Release 2025-02-11, Version 22.14.0 'Jod' (LTS), @aduh95 · nodejs/node · GitHub
New
PragmaticBookshelf
As digital systems increasingly run the world, mastery of the recurring patterns of software development risk is the key to fast and effe...
New
PragmaticBookshelf
Lint your docs like code: turn any style guide into enforceable rules with Vale and publish clear, consistent content every time. ...
New