Mongoose プロジェクト間でスキーマを共有する

大規模な組織では、複数のプロジェクト間で共有されるスキーマが含まれるプロジェクトを持つことが一般的です。たとえば、会社にプライベート npm パッケージ @initech/shared-schemas があり、npm list が次のように表示されるとします。

@initech/web-app1@1.0.0
├── @initech/shared-schemas@1.0.0
├── mongoose@8.0.1

上記の出力では、@initech/web-app1クライアントプロジェクトで、@initech/shared-schemas共有ライブラリです。

Mongoose をピア依存関係として配置する

最初に、そして最も重要な点として、@initech/shared-schemas共有スキーマの peerDependencies に Mongoose を記載し、上位の依存関係として記載しないことをお勧めします。たとえば、@initech/shared-schemaspackage.json は次のようになります。

{
  "name": "@initech/shared-schemas",
  "peerDependencies": {
    "mongoose": "8.x"
  }
}

このアプローチをお勧めする理由は次のとおりです。

  1. アップグレードが簡単です。たとえば、@initech/shared-schemas が Mongoose 8 に依存し、@initech/web-app1 が Mongoose 8 で適切に動作すると仮定します。ただし、@initech/web-app2 が Mongoose 7 からアップグレードできません。ピア依存関係を使用すると、共有スキーマに依存するプロジェクトが、Mongoose モジュールのバージョンの競合を起こすことなく、Mongoose のどのバージョンを使用する必要があるかを判断しやすくなります。
  2. Mongoose モジュールの重複のリスクを低減します。あるバージョンの Mongoose のスキーマとモデルを別のバージョンの Mongoose で使用することはサポートされていません。

モデルではなく、スキーマをエクスポートする

@initech/shared-schemas では Mongoose スキーマをエクスポートすることをお勧めします。これはモデルではありません。このアプローチはより柔軟であり、クライアントプロジェクトがお好みのパターンを使用してモデルをインスタンス化できます。特に @initech/shared-schemasmongoose.model() を使用して登録されたモデルをエクスポートする場合、そのモデルを別の接続に転送する方法はありません。

// `userSchema.js` in `@initech/shared-schemas`
const userSchema = new mongoose.Schema({ name: String });

// Do this:
module.exports = userSchema;

// Not this:
module.exports = mongoose.model('User', userSchema);

回避策: POJO をエクスポートする

既存の共有ライブラリが上記のベストプラクティスに従わない場合があります。古いバージョンの Mongoose に依存する共有ライブラリがある場合は、スキーマやモデルではなく POJO をエクスポートするという回避策が役立ちます。これにより、共有ライブラリの Mongoose のバージョンとクライアントプロジェクトの Mongoose のバージョンとの間の競合がなくなります。

// Replace this:
module.exports = new mongoose.Schema({ name: String });

// With this:
module.exports = { name: String };

そして、クライアントプロジェクトを更新して次のようにします。

// Replace this:
const { userSchema } = require('@initech/shared-schemas');

// With this:
const { userSchemaDefinition } = require('@initech/shared-schemas');
const userSchema = new mongoose.Schema(userSchemaDefinition);