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つの用途で使用されます:
defaultTaxZone: チャネルのデフォルト税計算エリアdefaultShippingZone: チャネルのデフォルト配送エリア
各チャネルは、これらのゾーンを参照して税計算と配送方法を決定します。
チャネルとの関係¶
チャネル(Channel)は、defaultTaxZoneId と defaultShippingZoneId を持ち、ゾーンを参照します:
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つのエンティティで構成されます:
- TaxCategory(税カテゴリ): 商品に適用する税の種類を定義
- 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 を使用していますが、本番環境では実際の決済ゲートウェイハンドラーに置き換える必要があります。
実装時の注意事項¶
チャネルとゾーンの依存関係¶
- チャネルを作成する際は、必ず
defaultTaxZoneIdとdefaultShippingZoneIdを指定する必要があります - ゾーンを削除する前に、そのゾーンを参照しているチャネルがないか確認してください
RequestContextの管理¶
- すべてのサービスメソッドは、最初の引数として
RequestContextを受け取る必要があります RequestContextは、リクエスト全体を通じて同じトランザクションを共有します- チャネル情報は
ctx.channelまたはctx.channelIdでアクセスできます
データの整合性¶
- ゾーン、チャネル、国の設定は、初期データ(
initial-data.ts)で一貫性を保つ必要があります - マイグレーションでこれらの設定を変更する場合は、既存データとの整合性に注意してください
関連ドキュメント¶
- Vendure開発ガイド: Vendureの基本概念と開発環境セットアップ
- Vendure開発ハンドブック: アーキテクチャとコアコンセプトの詳細
- Vendure Dashboard操作ガイド: 運用者向け操作ガイド
- Vendure公式ドキュメント: 公式ドキュメント(英語)
参照元コード¶
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開発チーム