ホテルグループ向けWebサイト: Next.jsを使用したマルチプロパティアーキテクチャ
地域統括部長が午後4時にメールを送信します。チャールストンの新しくて買収したブティックホテルは8週間でライブになります。ブランド基準は必須です。予約エンジンを統合する必要があります。例外なしです。あなたは現在のマルチサイト構成を開く—17のWordPressインストール、それぞれが独自のプラグインの迷宮を持ち、それぞれが更新後に異なる方法で破損し、それぞれが別のステージング環境が必要です。開発チームは最低でも12週間と推定します。私は8つ、25つ、さらには47の施設を運営するホテルグループで、この正確なシナリオが発売タイムラインを台無しにするのを見てきました。あなたが今日行うアーキテクチャの決定は、あなたの次の施設が3日で発売されるか3ヶ月で発売されるかを決定します。ほとんどのグループは安全に感じるパスを選択します。その後、それを2年間解きほぐすのに費やします。すべての施設を混乱なく強化する単一のプラットフォームを可能にするNext.jsアプローチはここにあります。
より良い方法があります。単一のNext.jsアプリケーション—適切に設計されている—は、1つのコードベース、1つのデプロイメントパイプライン、1つのコンテンツ管理層からホテルグループのすべての施設にサービスを提供できます。各施設は独自のブランディング、独自のコンテンツ、独自のドメインを取得します。エンジニアリングチームはその正気を取り戻します。
この記事は、そのシステムを構築する方法を正確に分解します。理論ではなく—実際のホテルグループプロジェクトで使用した実際のアーキテクチャパターン。
目次
- ホテルグループが統一されたプラットフォームを必要とする理由
- アーキテクチャ概要: 1つのコードベース、多くの施設
- Next.jsのマルチテナンシーパターン
- ホテルグループ向けのヘッドレスCMS戦略
- 共有コンポーネント対施設レベルのカスタマイズ
- 予約エンジン統合
- ドメインルーティングと施設解決
- 大規模でのパフォーマンス
- 集中管理ダッシュボード
- デプロイメントとDevOps
- 実世界のコスト比較
- FAQ

ホテルグループが統一されたプラットフォームを必要とする理由
典型的なホテルグループのWebサイト状況は次のようなものです: 施設AはWordPressで2019年からのテーマで動作します。施設BはSquarespaceです。GMの甥がセットアップしたため。施設Cは、誰も触れたくない独自のPHPサイトを持っています。コーポレートサイトは完全に異なるプラットフォームにあります。
各施設の更新には異なるワークフローが必要です。ブランドの一貫性は絵に描いた餅です。SEO戦略は数ダースのドメインに断片化されており、共有の権限はありません。企業が新しいアメニティバッジを追加したり、予約ウィジェットを更新することを決定したとき、誰かがこの変更を15の異なる場所で行う必要があります。
コストが増加します:
- メンテナンスのオーバーヘッド: 各プラットフォームには独自のホスティング、セキュリティパッチ、プラグイン更新が必要です
- ブランドドリフト: 施設は徐々にブランドガイドラインから逸脱します
- 開発者のコンテキストスイッチング: あなたのチーム(または代理店)は複数のプラットフォーム全体で専門知識が必要です
- 遅い施設発売: 新しい買収はオンラインになるのに数ヶ月かかります
- 分析の断片化: ポートフォリオ全体のパフォーマンスの統一されたビューはありません
集中型マルチプロパティプラットフォームはこれらすべてを解決します。1つのコードベース。1つのデプロイメント。1つのCMS。設定を通じて配信される施設ごとのコンテンツとブランディング。個別のコードベースではなく。
アーキテクチャ概要: 1つのコードベース、多くの施設
ここが機能する高レベルのアーキテクチャです:
┌─────────────────────────────────────────────┐
│ CDN / Edge Network │
│ (Vercel, Cloudflare, Fastly) │
├─────────────────────────────────────────────┤
│ Next.js Application │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Property │ │ Property │ │ Property │ │
│ │ Resolver │ │ Theming │ │ Content │ │
│ │ Middleware│ │ Engine │ │ Fetcher │ │
│ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────┤
│ API Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Headless │ │ Booking │ │ Media │ │
│ │ CMS │ │ Engine │ │ CDN │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────┘
Next.jsアプリはレンダリングレイヤーとして機能します。ミドルウェアは、どの施設がリクエストされているか(ドメイン、サブドメイン、またはパス経由)を決定します。テーミングエンジンは施設固有のスタイルを適用します。コンテンツフェッチャーはヘッドレスCMSから施設スコープのコンテンツをプルします。
下流のすべて—CMS、予約エンジン、メディアストレージ—は施設識別子でクエリされます。その識別子はシステム全体を結合するスレッドです。
Next.jsのマルチテナンシーパターン
Next.jsのマルチテナンシーには3つの主要なアプローチがあります。各パターンにはトレードオフがあります。
パターン1: サブドメインベースのルーティング
各施設はサブドメインを取得します: grandplaza.hotelgroup.com、seasideresort.hotelgroup.com。
Next.jsミドルウェアはリクエストをインターセプトし、サブドメインを抽出し、施設設定を解決します:
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { getPropertyByDomain } from '@/lib/properties';
export function middleware(request: NextRequest) {
const hostname = request.headers.get('host') || '';
const subdomain = hostname.split('.')[0];
const property = getPropertyByDomain(subdomain);
if (!property) {
return NextResponse.redirect(new URL('/not-found', request.url));
}
// Inject property context into headers for downstream use
const response = NextResponse.next();
response.headers.set('x-property-id', property.id);
response.headers.set('x-property-slug', property.slug);
return response;
}
メリット: クリーンなURL、簡単な施設の分離、施設が個別のTLDを必要としない場合のSEOに適しています。
デメリット: SSLサーティフィケート管理、施設ごとのブランド独立性が低い。
パターン2: カスタムドメインマッピング
各施設は独自のドメインを持ちます: grandplazahotel.com、seasideresort.com。
これがほとんどのホテルグループが実際に望んでいるものです。ミドルウェアロジックは似ていますが、ドメイン検索テーブルに照合しています:
const DOMAIN_MAP: Record<string, string> = {
'grandplazahotel.com': 'grand-plaza',
'www.grandplazahotel.com': 'grand-plaza',
'seasideresort.com': 'seaside-resort',
'www.seasideresort.com': 'seaside-resort',
};
Vercelはプロジェクトごとにカスタムドメインをネイティブにサポートし、Pro計画で最大50のドメインをマップできます(2026年現在、月額$20)。より大きなポートフォリオの場合、彼らのエンタープライズプランはその制限を削除します。
メリット: 完全なブランド独立性、既存のドメイン権を保持します。
デメリット: DNS管理のオーバーヘッド、より複雑なSSLプロビジョニング。
パターン3: パスベースのルーティング
1つのドメインの下にあるすべての施設: hotelgroup.com/properties/grand-plaza、hotelgroup.com/properties/seaside-resort。
メリット: 実装が最も簡単で、SEOのためのドメイン権を統合できます。
デメリット: 施設ごとのブランドアイデンティティが低く、URLの構造は企業的に見えます。
| パターン | ブランド独立性 | SEO柔軟性 | 実装の複雑さ | 最適なケース |
|---|---|---|---|---|
| サブドメイン | 中程度 | 中程度 | 低い | 予算意識のあるグループ |
| カスタムドメイン | 高い | 高い | 中程度 | 確立されたブランド |
| パスベース | 低い | 高い(統合) | 最も低い | 新しいポートフォリオサイト |
ほとんどのホテルグループは、Social Animalで協力しているのは、カスタムドメインマッピングを選択する傾向があります。施設はドメインのブランド権を持ち、マーケティングチームは独立を望みます。

ホテルグループ向けのヘッドレスCMS戦略
CMSの選択はこのアーキテクチャを作成または破壊します。コンテンツレベルでマルチテナンシーをサポートするシステムが必要です—施設Aのエディタが施設Bのコンテンツを誤って変更できないという場所ですが、企業の管理者がすべてを管理できます。
よく機能するCMSオプション
Sanityは、ホテルグループにとって私の最高のピックです。ドキュメントレベルのアクセス許可、カスタムスタジオ構成、GROQ照会言語により、施設スコープのコンテンツ取得は簡単です。ワークスペースごとのビューを持つ単一のSanityスタジオを構築できます。価格は、チームプラン($99/月(2026年の価格))から始まり、大規模なコンテンツボリューム全体で十分に拡張されます。
Contentfulは、すでにそのエコシステムにいる場合に動作します。彼らのスペースレベルの隔離は施設にうまくマップされますが、費用がかかる可能性があります—プレミアムプランの各スペースはコストを追加します。大規模なホテルグループの必要性については、月額2,500ドル以上を見ています。
Strapi(自己ホスト)は予算オプションです。カスタムミドルウェアとロールベースのアクセス制御を使用してマルチテナンシーレイヤーを自分で構築する必要がありますが、座席ごとのライセンス費用はありません。
ヘッドレスCMSの選択プロセス全体については、ヘッドレスCMS開発ガイドをご覧ください。
ホテル向けのコンテンツモデリング
施設全体で動作するコンテンツモデルは以下の通りです:
// Sanity schema example
export const property = defineType({
name: 'property',
title: 'Property',
type: 'document',
fields: [
defineField({ name: 'name', type: 'string' }),
defineField({ name: 'slug', type: 'slug' }),
defineField({ name: 'domain', type: 'string' }),
defineField({ name: 'brand', type: 'reference', to: [{ type: 'brand' }] }),
defineField({ name: 'location', type: 'geopoint' }),
defineField({ name: 'theme', type: 'propertyTheme' }),
defineField({ name: 'bookingEngineId', type: 'string' }),
],
});
export const room = defineType({
name: 'room',
title: 'Room Type',
type: 'document',
fields: [
defineField({ name: 'property', type: 'reference', to: [{ type: 'property' }] }),
defineField({ name: 'name', type: 'string' }),
defineField({ name: 'description', type: 'blockContent' }),
defineField({ name: 'maxOccupancy', type: 'number' }),
defineField({ name: 'amenities', type: 'array', of: [{ type: 'reference', to: [{ type: 'amenity' }] }] }),
defineField({ name: 'gallery', type: 'array', of: [{ type: 'image' }] }),
],
});
キーパターン: すべてのコンテンツドキュメント参照はpropertyです。クエリは常に施設でフィルタします。エディタは施設のコンテンツのみが表示されます。企業の管理者がすべてを見ます。
共有コンポーネント対施設レベルのカスタマイズ
ここがアーキテクチャが興味深くなる場所です。コンポーネントの80%が施設全体で共有されることを望んでいますが、20%が施設ごとのカスタマイズを許可しています。
テーミングレイヤー
施設ごとにテーマ設定を作成して、コンポーネントシステムに供給します:
// types/theme.ts
export interface PropertyTheme {
colors: {
primary: string;
secondary: string;
accent: string;
background: string;
text: string;
};
typography: {
headingFont: string;
bodyFont: string;
};
logo: {
light: string;
dark: string;
};
borderRadius: 'none' | 'sm' | 'md' | 'lg';
heroStyle: 'fullbleed' | 'contained' | 'split';
}
Tailwind CSS v4(2025年リリース)は、そのCSS優先の構成とネイティブなテーマ関数サポートにより、これを大幅に簡単にします。レイアウトレベルでCSS接頭辞変数を設定して、すべてのコンポーネントを通じてカスケードさせることができます:
// app/layout.tsx
export default async function PropertyLayout({ children }: { children: React.ReactNode }) {
const property = await getCurrentProperty();
const theme = property.theme;
return (
<html
style={{
'--color-primary': theme.colors.primary,
'--color-secondary': theme.colors.secondary,
'--font-heading': theme.typography.headingFont,
'--font-body': theme.typography.bodyFont,
} as React.CSSProperties}
>
<body className="font-body text-text bg-background">
{children}
</body>
</html>
);
}
コンポーネント構成
共有コンポーネントはテーマトークンを受け入れて、分岐ロジックなしに施設ごとに異なるレンダリングを行います:
// components/HeroSection.tsx
export function HeroSection({ property }: { property: Property }) {
const heroConfig = property.theme.heroStyle;
const variants = {
fullbleed: 'h-screen w-full',
contained: 'h-[70vh] max-w-7xl mx-auto rounded-2xl overflow-hidden',
split: 'grid grid-cols-2 h-[80vh]',
};
return (
<section className={variants[heroConfig]}>
{/* Shared hero content structure */}
</section>
);
}
予約エンジン統合
ホテルのWebサイトは1つの理由で存在します。予約を駆動するため。予約エンジンの統合は非常に堅実である必要があります。
ほとんどのホテルグループは、これらの予約エンジンの1つを使用しています: SynXis(Sabre)、Pegasus、Bookassist、SiteMinder、または自有の中央予約システム。統合パターンはほぼ常に同じです。施設識別子、日付範囲、および大人数をパスして利用可能な状況を取得します。
// lib/booking.ts
export async function checkAvailability({
propertyCode,
checkIn,
checkOut,
adults,
children,
}: BookingQuery) {
const response = await fetch(`${BOOKING_ENGINE_URL}/availability`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${BOOKING_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
hotel_code: propertyCode,
arrival: checkIn,
departure: checkOut,
guests: { adults, children },
}),
});
return response.json();
}
予約ウィジェット自体では、2つのオプションがあります:
- 埋め込みiframe: 予約エンジンはウィジェットを提供し、埋め込みます。最も少ない仕事、最小限のコントロール。
- API駆動のカスタムUI: 検索と結果のUIを構築し、予約APIを直接呼び出し、支払いのためのみ予約エンジンに渡します。より多くの仕事、はるかに良いUX。
オプション2は、Next.jsアーキテクチャが本当に輝く場所です。美しく、速く、ブランド化された予約体験を構築できます。それは各施設にネイティブに感じます。サーバーコンポーネントは利用可能なデータを事前にフェッチできます。予約フローはあなたのドメインに留まります。これは、コンバージョン追跡とSEOの方が優れています。
ドメインルーティングと施設解決
施設解決フローは高速である必要があります。本当に速く。すべてのリクエストで実行されます。
ここが本番環境で動作するパターンです:
- Edgeミドルウェアは、ドメインを解決→施設スラッグ(メモリ内検索、サブミリ秒)
- 施設設定は、Vercel Edge ConfigまたはCloudflare KVを使用してエッジでキャッシュされます
- 完全な施設データ(テーマ、ナビゲーション、フッターコンテンツ)は、ISRを介して1回ビルドされるか、キャッシュされたリクエスト時にフェッチされます
// lib/property-resolver.ts
import { get } from '@vercel/edge-config';
export async function resolveProperty(hostname: string): Promise<PropertyConfig | null> {
// First: check edge config (sub-5ms)
const domainMap = await get<Record<string, string>>('domain-map');
const propertySlug = domainMap?.[hostname];
if (!propertySlug) return null;
// Second: get full property config (cached)
const propertyConfig = await get<PropertyConfig>(`property:${propertySlug}`);
return propertyConfig;
}
Vercel Edge Configはこの用途に最適です。これはグローバルに分散されたキー値ストアであり、読み取りレイテンシは1ミリ秒未満です。Pro計画では512KBのデータまで0ドルかかります。これは施設検索テーブルに十分です。
大規模でのパフォーマンス
ホテルのWebサイトは、重要な特定のパフォーマンス特性があります:
- 画像が重い場合: ルームギャラリー、施設写真、目的地の画像
- 季節的なトラフィックスパイク: 休日、会議シーズン、地元のイベント
- グローバルな観客: 世界中どこからでも閲覧している国際旅行者
- コンバージョン対応: 読み込み時間の100msはすべての予約をコストします
静的生成戦略
施設ページでは、増分静的再生成(ISR)を使用してください。ホテルコンテンツは毎分変わりません—60秒の再検証期間は通常問題ありません:
// app/[propertySlug]/page.tsx
export async function generateStaticParams() {
const properties = await getAllProperties();
return properties.map((p) => ({ propertySlug: p.slug }));
}
export const revalidate = 60;
30の施設グループで、施設あたり約20ページの場合、約600ページを事前生成しています。Next.jsはこれを汗をかくことなく処理します。ビルド時間は5分以下のままです。
画像の最適化
Next.js Image コンポーネントと、リモートローダーは施設ごとの画像最適化を処理します。Sanityを使用している場合、自動フォーマット変換とサイズ変更を伴うそれらの画像CDNは優れています。Cloudinaryは、Plusプラン(月額89ドル)でもう1つの堅い選択です。
典型的なホテル施設ページは、以下をターゲットとする必要があります:
- 4Gの接続で2.5秒未満のLCP
- 0のCLSloading画像からのレイアウトシフトなし)
- 初回読み込み時の総ページ重量が1.5MB未満
集中管理ダッシュボード
CMS以外に、ホテルグループは運用ダッシュボードが必要です。これはカスタムツールを構築する場所です:
- 施設概要: 各施設サイトのステータス(ライブ、ステージング、メンテナンス)
- コンテンツの鮮度: どの施設が季節コンテンツを更新していないか
- パフォーマンス監視: 施設あたりのコアWeb Vitals
- 分析ロールアップ: 全施設全体の予約ファンネルメトリクス
通常、これを別のNext.js アプリとして構築します(しばしばApp Routerのサーバー側機能とともに)。これは同じデータソースに接続されます。管理ダッシュボードは内部ツール—派手である必要がない場合がありますが、機能する必要があります。
デプロイメントとDevOps
1つのコードベースは1つのCI/CDパイプラインを意味します。デプロイメントフロー:
- コード変更: PR→レビュー→メインにマージ
- ビルド: Next.jsすべての施設全体で静的ページをビルドします
- デプロイ: Vercel(または同様)はエッジネットワークにデプロイします
- DNS: 各施設ドメインはデプロイメントをポイントします
コンテンツの変更はデプロイメントを必要としません。ヘッドレスCMSはWebhook経由でISR再検証をトリガーします:
// app/api/revalidate/route.ts
export async function POST(request: Request) {
const body = await request.json();
const { propertySlug, contentType } = body;
// Revalidate specific paths for the changed property
revalidatePath(`/${propertySlug}`);
if (contentType === 'room') {
revalidatePath(`/${propertySlug}/rooms`);
}
return Response.json({ revalidated: true });
}
実世界のコスト比較
20施設のホテルグループの実際のコストを比較してみましょう:
| コストカテゴリ | 個別サイト(WordPress) | 統一されたNext.jsプラットフォーム |
|---|---|---|
| ホスティング(毎月) | $2,000-4,000(20×管理WP) | $150-400(Vercel Pro/Team) |
| CMSライセンス | $0-600(施設あたりのプラグイン) | $99-300(Sanity/Contentful) |
| SSLサーティフィケート | $0-400(Let's Encryptを使用していない場合) | $0(自動プロビジョニング) |
| メンテナンス(年間) | $40,000-80,000(更新、セキュリティ) | $10,000-20,000 |
| 新しい施設の発売 | $5,000-15,000/施設 | $500-2,000(コンテンツ+設定) |
| 年間合計(推定) | $75,000-150,000 | $15,000-35,000 |
数字は近いものさえありません。これはまた、開発者の経験の改善を説明していません—1つのコードベースを持つことはあなたのチームが実際にシステムを理解していることを意味します。もう「その施設はどのWordPressバージョンで実行されていますか?」ではありません。
このアプローチを検討しているホテルグループでは、Next.js開発機能を概説しており、より詳細な推定のために価格構造を確認できます。
FAQ
ホテルグループを統一されたNext.jsプラットフォームに移行するのにどのくらい時間がかかりますか?
10〜20の施設グループの場合、キックオフから完全な発売まで3〜5ヶ月をお待ちください。最初の施設は最長です(8〜10週間)。なぜなら、プラットフォームを構築しているからです。その後の各施設は、主にコンテンツ移行とテーマ設定であり、1〜2週間かかります。通常、波状で発売します—一度に3〜4施設。
個々の施設は、他の施設にはないユニークなページを持つことができますか?
絶対に。コンテンツモデルは、施設固有のページタイプをサポートしています。リゾート施設に「ウェディングベニュー」セクションが必要な場合。しかし、ビジネスホテルはそうしません。これは、コンテンツレベルの決定です。CMS スキーマはオプションのページタイプをサポートし、Next.jsダイナミックルーティングは施設のCMS内に存在するページをレンダリング処理します。
新しいホテルを買収してプラットフォームに追加する必要がある場合はどうなりますか?
これは最大の勝利の1つです。新しい施設を追加することは、CMS内に施設エントリを作成し、テーマを構成し(色、フォント、ロゴ)、ドメインマッピングを追加してコンテンツを入力することを意味します。有能なコンテンツチームは、1〜2週間で新しい施設をライブにできます。1施設で2〜3ヶ月かけてスタンドアロンのWebサイトを構築するのと比較してください。
複数言語のサポートを異なる国の複数の施設全体でどのように処理しますか?
Next.jsには、組み込みのi18nルーティングサポートがあります。ローカライズされたコンテンツをサポートするヘッドレスCMS(SanityとContentfulの両方)と組み合わせて、各施設を関連言語で提供できます。バルセロナの施設は、スペイン語、カタロニア語、英語、フランス語が必要な場合があります。マイアミの施設は英語とスペイン語だけが必要な場合があります。各施設の言語設定は独立しています。
このアーキテクチャはAstroの代わりにNext.jsで機能しますか?
はい。そして、いくつかのホテルグループではそれが実際にはより良い選択です。施設が主にコンテンツ駆動(複雑な予約フロー例えば)で最小限のインタラクティビティを備えている場合、Astroのマルチページアーキテクチャはさらに優れたパフォーマンスでより少ないJavaScriptを提供できます。マルチテナンシーパターンは似ています—ミドルウェアベースの施設解決、施設スコープのヘッドレスCMS、施設ごとのテーマトークン。
施設が個別のドメインにあるが、1つのアプリケーションから提供される場合、SEOをどのように処理しますか?
各施設ドメインは独自のサイトマップ、独自のrobots.txt、独自の構造化データ(ホテルスキーママークアップ)、および独自のメタタグを取得します。Googleの観点からは、これらは完全に個別のWebサイトです。正規のURLは各施設独自のドメインをポイントします。また、集中型スキーママークアップ生成の利点も得られます—すべての施設は自動的にホテル、客室、レビュー、およびローカルビジネス情報の適切なJSON-LDを取得します。
ローカルアクティビティ予約やスパ予約システムなど、施設固有の統合をどのように処理しますか?
コンポーネントアーキテクチャは、施設レベルの統合設定をサポートします。CMS内の各施設設定は、どのサードパーティ統合を使用しているかを指定できます。レンダリングレイヤーは条件付きでそれらの統合コンポーネントを含みます。スパ施設はスパ予約ウィジェットを取得します。ダウンタウンビジネスホテルは会議室コンフィグレータを取得します。これらは動的インポートとしてロードされます。それらを使用しない施設に対するバンドルサイズに影響しません。
1つの施設のトラフィックスパイクが他の施設に影響を与えるリスクはありますか?
VercelまたはCloudflare Pagesなどのプラットフォームでは、本当にではありません。これらのエッジプラットフォームはトラフィックスパイクのために設計されています。静的ページはCDNキャッシュから提供されます。スパイクは、別の施設に影響を与えるサーバーリソースを消費することはありません。動的ルート(リアルタイム利用可能性チェック)では、1つの施設のウイルスモーメントが予約エンジンAPIクォータを使い果たすのを防ぐために、施設ごとのレート制限が必要になります。ただし、それはAPI級の問題です。ホスティング上の懸念ではありません。