← Về thư mục
📄 / / projects / blog / vwg-blog-v2 / ARCHITECTURE.md

🏛️ Vietnam Wedding Guide (VWG) — Architecture & System Design

Production Platform: weddinginvietnam.com & vietnamweddingguide.com
Headless CMS Studio: studio.weddinginvietnam.com
Framework Stack: Next.js 16 (App Router, React 19) + Sanity Studio v3 + Tailwind CSS v4 (CSS-first)


1. System Topology & Architecture Overview

Vietnam Wedding Guide is designed as a hybrid luxury editorial publication and private advisory platform. It combines the strict aesthetic fidelity of bespoke editorial layouts with the flexibility of a headless CMS.

flowchart TD
    subgraph Client["Users & Stakeholders"]
        Couples["International Destination Couples"]
        B2B["Resorts & Hospitality Partners"]
        Editors["Content & Advisory Team"]
    end

    subgraph NextJS["Frontend Layer (Next.js 16 App Router)"]
        Router["App Router Engine"]

        subgraph RenderingStrategy["Dual-Rendering Pattern"]
            direction TB
            CheckBlocks{"page.blocks > 0?"}
            BespokeViews["Bespoke Editorial Views<br/>(components/templates/*)"]
            PageBuilder["Page Builder Blocks<br/>(components/blocks/*)"]
        end

        subgraph SharedSystems["Shared UI & Logic"]
            FounderBio["FounderBioSection (4 variants)"]
            CTABanner["LuxuryCTABanner"]
            SiteConstants["site.ts & partnership.ts"]
            WALib["whatsapp.ts (Concierge Deep-Links)"]
        end
    end

    subgraph SanityCMS["Data & Content Layer (Sanity v3)"]
        Studio["Sanity Studio (studio.weddinginvietnam.com)"]
        Docs["Documents: author, post, service, venue, faq, testimonial, settings"]
        GROQ["Type-safe GROQ Queries + sanity-typegen"]
    end

    Editors -->|Manage Content & Orders| Studio
    Studio --> Docs
    Docs -->|Live Cache / Draft Mode| GROQ
    GROQ --> Router
    Router --> CheckBlocks
    CheckBlocks -->|Yes: Custom Page Override| PageBuilder
    CheckBlocks -->|No: Default Luxury View| BespokeViews
    BespokeViews --> SharedSystems
    PageBuilder --> SharedSystems
    SharedSystems --> Couples
    SharedSystems --> B2B

2. The Unified Dual-Rendering Strategy

To ensure 100% Luxury Editorial visual fidelity while retaining CMS autonomy for marketing campaigns, the platform employs a Hybrid Dual-Rendering Pattern:

Mode Trigger Target Component Purpose
Default Mode (Bespoke Editorial View) !page?.blocks \|\| page.blocks.length === 0 components/templates/*-view.tsx Handcrafted, high-conversion luxury pages (/, /about, /services, /contact, /partnership, /blog). Receives structured, typed data from Sanity documents.
Override Mode (Page Builder Blocks) page?.blocks && page.blocks.length > 0 components/blocks/index.tsx Marketing and editorial team can build custom landing pages or completely override core page sections via Sanity Studio without modifying code.
Catch-all Fallback Any slug without a dedicated route file app/(main)/[...slug]/page.tsx Dynamically renders arbitrary new CMS pages created by editors.

Core Route Mapping

frontend/app/(main)/
├── page.tsx                     → page.blocks ? <Blocks /> : <HomepageView />
├── about/page.tsx               → page.blocks ? <Blocks /> : <AboutView />
├── services/page.tsx            → page.blocks ? <Blocks /> : <ServicesView />
├── services/[slug]/page.tsx     → Dynamic route for individual service: <ServiceDetailView />
├── contact/page.tsx             → page.blocks ? <Blocks /> : <ContactView />
├── partnership/page.tsx         → page.blocks ? <Blocks /> : <PartnershipView />
├── blog/page.tsx                → Editorial index: <BlogIndexView />
├── blog/[slug]/page.tsx         → Individual article: <BlogArticleView />
└── [...slug]/page.tsx           → Universal CMS fallback: <Blocks />

3. Sanity Content Schemas Hierarchy

All documents in Sanity Studio are strongly typed and generate TypeScript definitions into frontend/sanity.types.ts via ./node_modules/.bin/sanity typegen generate.

classDiagram
    class Settings {
        siteName: string
        tagline: string
        contactEmail: string
        whatsapp: string
        socialLinks: array
        logo: object
        copyright: string
    }

    class Author {
        name: string
        slug: slug
        role: string
        bio: text
        image: image
        socialLinks: array
    }

    class Category {
        title: string
        slug: slug
        description: text
    }

    class Venue {
        name: string
        region: string
        logo: image
        logoUrl: string
        url: url
        width: number
        height: number
        orderRank: string
    }

    class Service {
        title: string
        slug: slug
        targetAudience: string
        tagline: string
        shortDescription: text
        pricing: string
        features: array
        packages: array
        processSteps: array
        faqs: array[reference:faq]
        coverImage: image
        ctaText: string
        ctaUrl: string
    }

    class Post {
        title: string
        slug: slug
        excerpt: text
        image: image
        body: block-content
        author: reference[Author]
        categories: array[reference:Category]
        publishedAt: datetime
    }

    class FAQ {
        title: string
        body: block-content
        orderRank: string
    }

    class Testimonial {
        name: string
        title: string
        rating: number
        image: image
        body: block-content
        orderRank: string
    }

    Post --> Author : written by
    Post --> Category : classified under
    Service --> FAQ : includes faqs

4. Shared Section Architecture & Design Tokens

4.1. Centralized Constants

To maintain strict DRY (Don't Repeat Yourself) compliance and avoid scattered hardcoded values: - frontend/lib/constants/site.ts: - SITE_DOMAINS: Dual-domain pairing (weddinginvietnam.com and vietnamweddingguide.com). - FOUNDER_CREDENTIALS: Founder Nhi Võ credentials ("9+ Years Hospitality", InterContinental background, ex-Commercial Head at Je t'aime Art, portfolio https://nhivo.io.vn). - CONTACT_INFO: Official contact email (info@weddinginvietnam.com) and on-site operational regions. - frontend/lib/constants/partnership.ts: - PARTNERSHIP_TIERS: Pricing ($222/mo Standard, $444/mo Pro), initial 6-month fit review commitments ($1,332 / $2,664 USD), and deliverable checklists. - UHNW_ADVISORY_CONFIG: Hourly advisory rate ($111/hour), founder advisory scope, and deliverables.

4.2. Reusable Modular Components


5. Strict Editorial & Visual Guardrails

When updating code, schemas, or templates, the following constraints are mandatory:

  1. No Decorative Emojis/Icons Before Badges:
  2. ❌ Banned: ✨ Private Advisory, 💍 Wedding Guide
  3. ✅ Required: Private Advisory (uppercase, tracking [0.25em], bordered by luxury gold lines <span className="w-8 h-px bg-brand-gold/60" />).
  4. Founder Credentials:
  5. Must strictly specify "9+ Years Hospitality" for Nhi Võ with IHG / InterContinental pedigree.
  6. Dual-Domain Ecosystem:
  7. Always reference both primary (weddinginvietnam.com) and secondary (vietnamweddingguide.com) domains in brand/partnership contexts.
  8. Zero Working Hours Policy:
  9. Never display working hours or business hours (e.g., "9:00 - 18:00" or "within 24 business hours"). VWG operates as an asynchronous global advisory accommodating international timezones (US, Europe, Australia, Asia). All response commitments must use "within 4–12 hours" or "promptly".

6. Content Editor Standard Operating Procedure (SOP)

How to Add / Update Venues

  1. Open Sanity Studio (studio.weddinginvietnam.com).
  2. Navigate to Venues in the desk menu.
  3. Click Create Venue and fill in:
  4. Venue Name: Official resort/hotel name.
  5. Region: "Da Nang", "Phu Quoc", "Saigon", or "Nha Trang".
  6. Logo Image: Upload transparent PNG/WebP logo.
  7. Website URL: Direct link to property's destination wedding page.
  8. Drag and drop in the desk list to change the display sequence in the homepage marquee track.

How to Add / Edit Services

  1. Navigate to Services in the Studio desk.
  2. Fill in the core fields: Title, Slug, Target Audience (couples vs professionals), Tagline, Pricing, and Included Deliverables.
  3. Add nested Packages, Process Steps, and link relevant FAQs.
  4. Publish. The service will immediately update both /services grid and /services/[slug] detail page.

How to Publish an Editorial Guide

  1. Navigate to Posts in the Studio desk.
  2. Fill in Title, Slug, Excerpt, Cover Image, Author (select Nhi Võ), Categories, and Published Date.
  3. Write body content using Portable Text, Callout Boxes, and Gallery blocks.
  4. Publish. The guide will appear on /blog and be indexed for Google & AI search engines.