コンテンツにスキップ

Vendureコアコンセプト詳細解説

概要

このドキュメントは、Vendureのコアコンセプト(ゾーン、チャネル等)の技術的な詳細を解説する開発者向けドキュメントです。プロジェクト内の実装コードを基に、実際の設定値と使用方法を記載しています。

運用者向けドキュメント: 非技術者の運用担当者向けの操作ガイドは Vendure Dashboard操作ガイド を参照してください。

ゾーン(Zone)の技術詳細

概念とアーキテクチャ

ゾーン(Zone)は、配送エリア税計算エリアを定義するためのVendureのコアコンセプトです。ゾーンは1つ以上の国(Country)を含み、以下の用途で使用されます:

  • 配送ゾーン(Shipping Zone): 配送方法が利用可能な地域を定義
  • 税ゾーン(Tax Zone): 税率を適用する地域を定義

本プロジェクトでの実装

apps/vendure-server/src/data/initial-data.ts で以下のように設定されています:

zones: [
  {
    name: 'Japan',
    memberCodes: ['JP'],
  },
],

現在、「Japan」ゾーン1つのみを設定しており、日本(JP)のみを含んでいます。

ゾーンの種類

Vendureでは、ゾーンは以下の2つの用途で使用されます:

  1. defaultTaxZone: チャネルのデフォルト税計算エリア
  2. defaultShippingZone: チャネルのデフォルト配送エリア

各チャネルは、これらのゾーンを参照して税計算と配送方法を決定します。

チャネルとの関係

チャネル(Channel)は、defaultTaxZoneIddefaultShippingZoneId を持ち、ゾーンを参照します:

type Channel implements Node {
  id: ID!
  defaultTaxZone: Zone
  defaultShippingZone: Zone
  # ... その他のプロパティ
}

GraphQL APIでの操作

ゾーンの取得

query GetZones {
  zones {
    items {
      id
      name
      members {
        id
        code
        name
      }
    }
  }
}

ゾーンの作成(Admin API)

mutation CreateZone($input: CreateZoneInput!) {
  createZone(input: $input) {
    id
    name
  }
}

コード例

ゾーン情報を取得する例:

import { RequestContext, ZoneService } from '@vendure/core';

@Injectable()
export class MyService {
  constructor(private zoneService: ZoneService) {}

  async getZones(ctx: RequestContext) {
    const zones = await this.zoneService.findAll(ctx);
    return zones.items;
  }
}

チャネル(Channel)の技術詳細

概念とマルチテナントアーキテクチャ

チャネル(Channel)は、Vendureのマルチテナントアーキテクチャの中核となる概念です。各チャネルは独立した以下の要素を持ちます:

  • 商品カタログ: チャネルごとに異なる商品を表示可能
  • 価格設定: チャネルごとに異なる価格を設定可能
  • 在庫管理: チャネルごとに独立した在庫管理(将来実装予定)
  • 言語・通貨: チャネルごとに異なる言語・通貨を設定可能
  • 税・配送設定: チャネルごとに異なるゾーンを参照可能

本プロジェクトでの実装

現在、リツビプロジェクトではデフォルトチャネル1つのみを使用しています。初期データでは、Vendureの標準的な初期化プロセスでデフォルトチャネルが作成されます。

チャネルの設定は apps/vendure-server/src/vendure-config.shared.ts で管理されています:

const vendureConfig: VendureConfig = {
  defaultLanguageCode: LanguageCode.ja,
  // ... その他の設定
};

チャネルの主要プロパティ

GraphQLスキーマから、チャネルの主要プロパティは以下の通りです:

type Channel implements Node {
  id: ID!
  code: String!                    # チャネル識別コード
  token: String!                   # 認証トークン
  defaultTaxZone: Zone             # デフォルト税ゾーン
  defaultShippingZone: Zone        # デフォルト配送ゾーン
  defaultLanguageCode: LanguageCode! # デフォルト言語
  availableLanguageCodes: [LanguageCode!] # 利用可能な言語
  defaultCurrencyCode: CurrencyCode! # デフォルト通貨
  availableCurrencyCodes: [CurrencyCode!]! # 利用可能な通貨
  pricesIncludeTax: Boolean!       # 価格に税を含むか
  seller: Seller                   # 販売者(マルチベンダー対応)
  customFields: JSON               # カスタムフィールド
}

RequestContextとの関係

RequestContextは、すべてのVendure操作の中心となるオブジェクトで、チャネル情報を含みます:

interface RequestContext {
  // チャネル情報
  channel: Channel;
  channelId: ID;

  // 言語情報
  languageCode: LanguageCode;

  // ユーザー情報
  user?: User;
  activeUserId?: ID;

  // 認証情報
  isAuthorized: boolean;
  apiType: 'shop' | 'admin';
}

すべてのサービスメソッドは、最初の引数としてRequestContextを受け取り、チャネル情報にアクセスできます:

@Injectable()
export class MyService {
  async getData(ctx: RequestContext, id: string) {
    // ctx.channelId でチャネルを識別
    // ctx.channel でチャネルオブジェクトにアクセス
    // ctx.languageCode で言語を識別

    return this.connection
      .getRepository(ctx, MyEntity)
      .findOne({ where: { id, channelId: ctx.channelId } });
  }
}

GraphQL APIでの操作

チャネルの取得

query GetChannels {
  channels {
    items {
      id
      code
      defaultTaxZone {
        id
        name
      }
      defaultShippingZone {
        id
        name
      }
      defaultLanguageCode
      defaultCurrencyCode
      pricesIncludeTax
    }
  }
}

チャネルの作成(Admin API)

mutation CreateChannel($input: CreateChannelInput!) {
  createChannel(input: $input) {
    ... on Channel {
      id
      code
    }
    ... on LanguageNotAvailableError {
      errorCode
      message
      languageCode
    }
  }
}

CreateChannelInputの例:

const input: CreateChannelInput = {
  code: 'new-channel',
  token: 'channel-token',
  defaultLanguageCode: LanguageCode.ja,
  availableLanguageCodes: [LanguageCode.ja],
  pricesIncludeTax: false,
  defaultCurrencyCode: CurrencyCode.JPY,
  availableCurrencyCodes: [CurrencyCode.JPY],
  defaultTaxZoneId: '1', // Zone ID
  defaultShippingZoneId: '1', // Zone ID
};

コード例

チャネル情報を取得する例:

import { RequestContext, ChannelService } from '@vendure/core';

@Injectable()
export class MyService {
  constructor(private channelService: ChannelService) {}

  async getChannels(ctx: RequestContext) {
    const channels = await this.channelService.findAll(ctx);
    return channels.items;
  }

  async getCurrentChannel(ctx: RequestContext) {
    return ctx.channel;
  }
}

RequestContextの作成例(シードデータなどで使用):

import { RequestContext, ChannelService, LanguageCode } from '@vendure/core';

// シードデータでの使用例
const channelService = app.get(ChannelService);
const channels = await channelService.findAll();
const defaultChannel = channels.items[0];

const requestContext = new RequestContext({
  apiType: 'admin',
  channel: defaultChannel,
  languageCode: LanguageCode.ja,
  isAuthorized: true,
  authorizedAsOwnerOnly: false,
});

国(Country)とゾーンの関係

データモデル

国(Country)は、ISO 3166-1 alpha-2コード(例:JP)で識別され、1つのゾーンに所属します。

本プロジェクトでの設定

apps/vendure-server/src/data/initial-data.ts で以下のように設定されています:

countries: [
  {
    code: 'JP',
    name: '日本',
    zone: 'Japan',
  },
],

現在、日本(JP)のみを設定しており、「Japan」ゾーンに所属しています。

複数国対応の拡張方法

将来的に複数国に対応する場合の拡張例:

zones: [
  {
    name: 'Japan',
    memberCodes: ['JP'],
  },
  {
    name: 'Asia',
    memberCodes: ['CN', 'KR', 'TW'],
  },
],

countries: [
  { code: 'JP', name: '日本', zone: 'Japan' },
  { code: 'CN', name: '中国', zone: 'Asia' },
  { code: 'KR', name: '韓国', zone: 'Asia' },
  { code: 'TW', name: '台湾', zone: 'Asia' },
],

税(Tax)システムの実装

TaxCategoryとTaxRateの関係

Vendureの税システムは、以下の2つのエンティティで構成されます:

  1. TaxCategory(税カテゴリ): 商品に適用する税の種類を定義
  2. TaxRate(税率): 税カテゴリに適用する実際の税率を定義

本プロジェクトでの設定

apps/vendure-server/src/data/initial-data.ts で以下のように設定されています:

// 税率
taxRates: [
  {
    name: '消費税(10%)',
    percentage: 10,
  },
  {
    name: '軽減税率(8%)',
    percentage: 8,
  },
  {
    name: '非課税(0%)',
    percentage: 0,
  },
],

// 税カテゴリ
taxCategories: [
  { name: '標準税率10%' },
  { name: '軽減税率8%' },
  { name: '非課税' },
],

チャネルごとの税設定の実装

チャネルは defaultTaxZone を参照し、そのゾーンに適用される税率が使用されます。商品に税カテゴリを設定することで、適切な税率が適用されます。

配送(Shipping)システムの実装

ShippingMethodとShippingZoneの関係

配送方法(ShippingMethod)は、特定の配送ゾーン(ShippingZone)に対して有効になります。チャネルは defaultShippingZone を参照し、そのゾーンで有効な配送方法が利用可能になります。

本プロジェクトでの設定

apps/vendure-server/src/data/initial-data.ts で以下のように設定されています:

shippingMethods: [
  {
    name: '通常配送',
    price: 880,
  },
  {
    name: '速達',
    price: 1500,
  },
],

カスタム配送計算プラグインとの連携

リツビプロジェクトでは、カスタム配送計算プラグイン(@ritsubi/shipping-calculator-plugin)を使用しています。このプラグインは、以下の要因で配送料金を計算します:

  • 商品のブランド(エクスビアンス、メソシューティカル、美容機器消耗品)
  • 配送先の都道府県
  • 注文金額(無料配送の閾値)
  • 直送モード(+10%の追加料金)

詳細は 配送計算プラグインのドキュメント を参照してください。

支払い(Payment)システムの実装

PaymentMethodの実装

支払い方法(PaymentMethod)は、PaymentMethodHandlerを実装したハンドラーを使用して処理されます。

本プロジェクトでの設定

apps/vendure-server/src/data/initial-data.ts で以下のように設定されています:

paymentMethods: [
  {
    name: '銀行振込',
    code: 'bank-transfer',
    handler: {
      code: dummyPaymentHandler.code,
      arguments: [{ name: 'automaticSettle', value: 'true' }],
    },
  },
  {
    name: 'クレジットカード',
    code: 'credit-card',
    handler: {
      code: dummyPaymentHandler.code,
      arguments: [{ name: 'automaticSettle', value: 'true' }],
    },
  },
  // ... その他の支払い方法
],

現在は dummyPaymentHandler を使用していますが、本番環境では実際の決済ゲートウェイハンドラーに置き換える必要があります。

実装時の注意事項

チャネルとゾーンの依存関係

  • チャネルを作成する際は、必ず defaultTaxZoneIddefaultShippingZoneId を指定する必要があります
  • ゾーンを削除する前に、そのゾーンを参照しているチャネルがないか確認してください

RequestContextの管理

  • すべてのサービスメソッドは、最初の引数としてRequestContextを受け取る必要があります
  • RequestContextは、リクエスト全体を通じて同じトランザクションを共有します
  • チャネル情報はctx.channelまたはctx.channelIdでアクセスできます

データの整合性

  • ゾーン、チャネル、国の設定は、初期データ(initial-data.ts)で一貫性を保つ必要があります
  • マイグレーションでこれらの設定を変更する場合は、既存データとの整合性に注意してください

関連ドキュメント

参照元コード

  • apps/vendure-server/src/data/initial-data.ts: 初期データ設定
  • apps/vendure-server/src/vendure-config.shared.ts: Vendure設定
  • packages/shared-graphql/schema/admin.graphql: Admin API GraphQLスキーマ
  • packages/shared-graphql/schema/shop.graphql: Shop API GraphQLスキーマ

更新日: 2025-01-XX
対象バージョン: Vendure 3.5.x
メンテナー: Ritsubi開発チーム