PortfolioOS CMS Architecture Lab
ActiveStarted October 2025· Updated June 2026

PortfolioOS CMS Architecture Lab

This lab documents the architecture decisions behind the PortfolioOS Platform—Prisma schema design, JSON seed pipelines, withDbFallback getters, and revalidateTag patterns that keep admin edits visible on the public site without redeploying.

Experiment signals

Prisma models
50+
Content aggregates
10
Public locales
2
Next.jsPrismaPostgreSQLTypeScriptnext-intlDocker

Research brief

Experiment objectives

  1. 1Model portfolio content in PostgreSQL with translation tables instead of monolithic JSON blobs
  2. 2Build a reproducible seed pipeline from generated JSON through lib/data.ts into prisma db seed
  3. 3Wire public getters with withDbFallback so pages survive partial DB migration
  4. 4Validate admin Server Actions + revalidateTag keep cached SSR pages fresh after edits
  5. 5Document content visibility rules for publish toggles, PDF links, and conditional UI sections

Key learnings

  • Translation tables with @unique([entityId, locale]) scale better than duplicating entire entities per language
  • content_section as a shared table for lab and article bodies reduces schema duplication
  • Granular cache tags (projects, labs, articles) beat full-site revalidation after admin saves
  • Domain types in lib/ decouple React components from Prisma—mappers absorb schema changes
  • Version-controlled generated JSON makes demo portfolios reviewable and diffable in PRs

Lab visuals

PortfolioOS CMS Architecture Lab — 2
1 / 4

Experiment log

Documented steps, architecture notes, and outcomes from the research process.

Lab Overview

This lab is the architectural companion to the PortfolioOS Platform project. Instead of treating the portfolio as static pages, we treat it as a small CMS: normalized tables, admin CRUD, cached reads, and structured long-form content for labs and articles.

The goal is a codebase you can deploy once per customer with admin-editable content and zero mock-data drift between seed, admin, and public pages.

Schema Design

Content aggregates—project, lab, article, product—each have a core row plus translation rows for locale-specific text. Nested relations (slides, challenges, metrics, sections) hang off the aggregate with explicit sortOrder columns.

Labs and articles share a content_section pattern: anchorId, heading, paragraphs[], optional list[], optional code block.

  • project + project_translation + slides/challenges/screenshots
  • lab + lab_translation + content_section + metrics
  • article + article_translation + content_section + tags
  • Singleton site tables: home_hero, about_value, quote_settings

Seed Pipeline

Content is authored in generated/generate-portfolio-content.py, exported to generated-portfolio-content.json, imported by lib/data.ts, and loaded into PostgreSQL via prisma/seed/content.ts.

This pipeline keeps demo data rich (case studies, labs, articles) while remaining reproducible: npm run db:reset restores the full showcase.

snippet
# Regenerate content JSON
python generated/generate-portfolio-content.py

# Reseed database
cd new && npm run db:seed

Cache Invalidation

Public getters wrap Prisma queries in unstable_cache with entity-specific tags. Admin Server Actions call revalidateTag after successful writes so the next SSR request fetches fresh data.

During migration, withDbFallback catches DB errors and serves lib/data.ts until seed counts verify.

Active Experiments

Current work explores schema-driven admin forms that auto-sync when Prisma models change, and moving quote calculator constants from lib/quote-data.ts into quote_settings rows for full CMS coverage.