🛠️ サービス
サービスは、ドメインの機能を合成し、defService を介して Express ルーターとしてマウントするファクトリー関数の命名規則です。サービス登録 API はありません。SDK は src/services/ からすぐに利用できるファクトリーをエクスポートします。
各ファクトリーは、HTTP(および任意で WebSocket)エンドポイントを持つ express.Router を返します。サービスは、アプリケーションコードから直接呼び出すインプロセスのドメイン API ではありません。
🔍 サービスとは
サービスは、1 つ以上の機能を 1 つのマウント可能なルーターへ束ねます。各機能は compose(withSchema(...), withRoute(...)) です。
schemas/*.ts + routes/*.ts → features/*.ts → services/*.ts → defService()
(withSchema/withRoute) (compose) (compose many) (router)
┌─────────────┐
│ Service │ identitiesService(dataStores, configuration)
├─────────────┤
│ Features ▷ getIdentityFeature
│ ▷ findIdentitiesFeature
│ ▷ updateIdentityFeature
│ ▷ ...
└─────────────┘
サービスを使用する理由:
- ドメインごとに 1 つのファクトリー。完全な REST(および任意の WS)サーフェスを 1 行でマウントできます。
- 依存関係は
partialを介して注入され、グローバル状態はありません。 - 合成可能です。同じパターンで機能をサービス間で再利用したり、カスタムファクトリーを構築したりできます。
- サービスごとに型付けされた
dataStoresとconfiguration(MongoDBCollectionインターフェース)を使用します。
単一の名前空間エクスポートからサービスをインポートします。
import { services } from '@nodeblocks/backend-sdk';
const { identitiesService, authService, chatService } = services;
機能の定義と合成については機能 »を参照してください。
⚙️ 仕組み(defService)
defService は合成された Composable を稼働する Express ルーターに変換します。提供されるすべてのサービスファクトリーは、同じ内部接続に従います。
defService(
partial(compose(...features), [{ dataStores, configuration, authenticate, ...drivers }]),
webSocketServer? // 現在これを渡すのは chatService のみ
);
リクエストを受け取ると、defService は次を行います。
- 合成済みアダプターを実行し、
ServiceDefinition(ルート、スキーマ、ミドルウェア、注入済み依存関係)を構築します。 express.json()の本文解析を備えた Express ルーターを作成します。- 合成済み定義にある任意の
service.middlewareを適用します。 - HTTP ルート(
GET/POST/PUT/PATCH/DELETE)を登録します。バリデーターはハンドラーより先に実行されます。 - 存在する場合、WebSocket ルートを指定の
webSocketServerにバインドします。
defService に webSocketServer を渡さない場合、WebSocket ルートはアサートします。chatService は、任意の第 3 引数にある webSocketServer プロパティを defService の第 2 引数として転送します。これは drivers 名前空間のエクスポートではありません。
検証または処理中にスローされたエラーは NodeblocksError に正規化され、next 経由で Express に渡されます。defService は最終エラー応答を送信しません。アプリケーションレベルで nodeBlocksErrorMiddleware() をマウントしてください。
応答処理({ statusCode, data } ターミネーター)についてはルート »、チャットストリーミングのセットアップについてはWebSocket サービスガイド »を参照してください。
🧑💻 標準ファクトリーパターン
すべての SDK サービスは同じ構造を共有します。
import { partial } from 'ramda';
import { Collection } from 'mongodb';
import { features, primitives, utils } from '@nodeblocks/backend-sdk';
const {
getIdentityFeature,
findIdentitiesFeature,
updateIdentityFeature,
deleteIdentityFeature,
lockIdentityFeature,
unlockIdentityFeature,
} = features;
const { compose, defService } = primitives;
const { getBearerTokenInfo, getCookieTokenInfo } = utils;
export const identitiesService = (
dataStores: { identities: Collection },
configuration: {
authSecrets: { authEncSecret: string; authSignSecret: string };
authMode?: 'bearer' | 'cookie';
identity?: { typeIds?: { admin: string; guest: string; regular: string } };
}
) => {
return defService(
partial(
compose(
getIdentityFeature,
findIdentitiesFeature,
updateIdentityFeature,
deleteIdentityFeature,
lockIdentityFeature,
unlockIdentityFeature
),
[
{
dataStores,
configuration,
authenticate:
configuration.authMode === 'cookie'
? getCookieTokenInfo
: getBearerTokenInfo,
},
]
)
);
};
partial と defService の動作:
partial(compose(...features), [deps])は、初期ServiceDefinitionとしてdataStores、configuration、authenticate、任意のドライバーを事前適用します。- 各機能コンポーザーがその定義にルート(およびスキーマメタデータ)を追加します。
defServiceが最終的なServiceDefinitionを Express ルーターへ変換します。
一般的に注入されるフィールド: dataStores、configuration、authenticate。
任意のドライバー(ファクトリーの第 3 引数で受け取り、サービスコンテキストに転送): mailService、fileStorageDriver、OAuth ドライバー(googleOAuthDriver、twitterOAuthDriver、lineOAuthDriver)、findAddressDriver。webSocketServer は別途処理されます。chatService はリクエストコンテキストに注入せず、defService の第 2 引数として渡します。
Cookie 認証(authMode: 'cookie')を使用する場合、サービスルーターをマウントする前に cookie-parser Express ミドルウェアを登録してください。
📑 SDK サービスカタログ
サービスは src/services/index.ts に対応する SDK の services 名前空間で、ドメインごとに整理されています。
| サービス | エクスポート | 責務 | ドライバー(第 3 引数) | ドキュメント |
|---|---|---|---|---|
| Authentication | authService | 登録、ログイン/ログアウト、MFA、トークン、招待、OAuth | mailService、OAuth ドライバー | ドキュメント » |
| Identity | identitiesService | ID の CRUD、ロック/ロック解除 | — | ドキュメント » |
| Profile | profileService | プロファイル、アバター、フォロー、「いいね」 | fileStorageDriver | ドキュメント » |
| Organization | organizationService | 組織、メンバー、変更リクエスト | fileStorageDriver | ドキュメント » |
| Product | productService | 製品 CRUD、バッチ/コピー | fileStorageDriver | ドキュメント » |
| Attributes | attributesService | 属性とグループ | — | ドキュメント » |
| Category | categoryService | カテゴリ CRUD とステータス | — | ドキュメント » |
| Location | locationService | 階層的なロケーション | — | ドキュメント » |
| Order | orderService | 注文管理 | — | ドキュメント » |
| Chat | chatService | チャンネル、メッセージ、テンプレート、添付ファイル、WS ストリーミング | fileStorageDriver、webSocketServer | ドキュメント » |
| Notification | notificationService | 通知の一覧、既読化(単一/一括) | — | ドキュメント » |
| Address | addressService | 住所検索 | findAddressDriver | ドキュメント » |
ℹ️
@nodeblocks/backend-sdkからはservices名前空間経由でサービスをインポートします。import { services } from '@nodeblocks/backend-sdk'。
設定に関する注意:
authServiceはPartial<AuthenticationServiceConfiguration>を受け取ります。デフォルトはgetMergedAuthConfig()により内部でマージされます。完全な設定範囲は認証サービスのドキュメントを参照してください。dataStoresの型では、一部の MongoDB コレクションが任意としてマークされています(例: 認証用のinvitations、onetimetokens、製品用のproductVariants、組織用のorganizationChangeRequests)。必要なコレクションは各サービスのドキュメントを参照してください。
🧑💻 サービスのマウント
すべてのサービスは、次のシグネチャを持つファクトリー関数です。
(dataStores, configuration, drivers?) => express.Router
任意の第 3 引数 drivers はサービスによって異なります。上記のカタログ表を参照してください。
基本 HTTP サービス
import express from 'express';
import { middlewares, services, drivers } from '@nodeblocks/backend-sdk';
const { nodeBlocksErrorMiddleware } = middlewares;
const { identitiesService } = services;
const { withMongo } = drivers;
const connectToDatabase = withMongo(
'mongodb://localhost:27017',
'dev',
'user',
'password'
);
express()
.use(
'/api/identities',
identitiesService(
{
identities: await connectToDatabase('identities'),
},
{
authSecrets: {
authEncSecret: 'your-encryption-secret',
authSignSecret: 'your-signing-secret',
},
authMode: 'bearer', // または 'cookie' — cookie-parser ミドルウェアが必要
identity: {
typeIds: {
admin: 'admin-type-id',
guest: 'guest-type-id',
regular: 'regular-type-id',
},
},
}
)
)
.use(nodeBlocksErrorMiddleware())
.listen(8089, () => console.log('Server running'));
ドライバーを使用するサービス
外部ドライバーを第 3 引数として渡す必要があるサービスもあります。
import { services } from '@nodeblocks/backend-sdk';
const { authService, chatService, profileService } = services;
app.use('/api/auth', authService(dataStores, config, {
mailService,
googleOAuthDriver,
twitterOAuthDriver,
lineOAuthDriver,
}));
app.use('/api/profiles', profileService(dataStores, config, {
fileStorageDriver,
}));
app.use('/api/chat', chatService(dataStores, config, {
fileStorageDriver,
webSocketServer, // streamChatMessagesFeature には必須
}));
🔧 カスタムサービスの構築
任意の機能サブセットを自身のファクトリーへ合成できます。SDK 提供のサービスも同じパターンを使用しています。
import { partial } from 'ramda';
import { features, primitives, utils } from '@nodeblocks/backend-sdk';
const { getIdentityFeature, findIdentitiesFeature } = features;
const { compose, defService } = primitives;
const { getBearerTokenInfo } = utils;
export const myIdentityService = (dataStores, configuration) =>
defService(
partial(
compose(getIdentityFeature, findIdentitiesFeature),
[{ dataStores, configuration, authenticate: getBearerTokenInfo }]
)
);
features 名前空間から機能を選択し、選択したルートで必要なドライバーのみを注入して、返されたルーターを Express にマウントします。機能合成の詳細は機能 »を参照してください。
📐 推奨事項
- サービスファクトリーごとに 1 ドメイン — 各 SDK 提供サービスは 1 つのビジネス領域を対象にします。
- 機能を合成し、ルートを手動で登録しない —
defServiceにより、合成済み機能のバリデーターとハンドラーを接続します。 partial経由で認証とドライバーを注入する — グローバルを避け、依存関係オブジェクトにauthenticate、mailService、fileStorageDriverなどを渡します。- エラーミドルウェアをアプリケーションレベルでマウントする —
nodeBlocksErrorMiddleware()はサービスファクトリーの一部ではなく、ホストアプリの責務です。 - ドライバー要件を確認する — ファイルアップロードルートには
fileStorageDriver、OAuth およびメールフローにはそれぞれのドライバー、チャット WebSocket ストリーミングにはwebSocketServerが必要です。
➡️ 次へ
認証サービスから始めるか、上記カタログの他のサービスを確認してください。コンポーネントレベルの詳細は、機能 »、ルート »、スキーマ »、ブロック »を参照してください。リアルタイムチャットについてはWebSocket サービスガイド »を確認してください。