コンフィグプロバイダ

コンフィグプロバイダ

一部の設定ファイル(config/hash.tsなど)は、設定を単なるオブジェクトとしてエクスポートするのではなく、コンフィグプロバイダとしてエクスポートされます。コンフィグプロバイダは、アプリケーションの起動後に設定を遅延計算するための透過的なAPIを提供します。

コンフィグプロバイダを使用しない場合

コンフィグプロバイダを理解するために、コンフィグプロバイダを使用しない場合のconfig/hash.tsファイルを見てみましょう。

import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
})
}
}

これまでは問題ありません。driversコレクションからscryptドライバーを参照する代わりに、直接インポートしてファクトリ関数を使用してインスタンスを返しています。

Scryptドライバーが値をハッシュするたびにイベントを発行するために、ScryptドライバーがEmitterクラスのインスタンスを必要とするとしましょう。

import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
import emitter from '@adonisjs/core/services/emitter'
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
}
}

🚨 上記の例は失敗します。なぜなら、AdonisJSのコンテナサービスは、アプリケーションが起動し、設定ファイルがインポートされる前に利用できないからです。

それはAdonisJSのアーキテクチャの問題ではありません 🤷🏻‍♂️

実際にはそうではありません。コンテナサービスを使用せずに、設定ファイル内でEmitterクラスのインスタンスを直接作成しましょう。

import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
import emitter from '@adonisjs/core/services/emitter'
import { Emitter } from '@adonisjs/core/events'
const emitter = new Emitter()
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
}
}

これで新たな問題が発生しました。Scryptドライバーのために作成したemitterインスタンスは、ドライバーが発行するイベントをインポートしてリッスンするためにグローバルに利用できません。

そのため、Emitterクラスの構築をそのファイルに移動し、そのインスタンスをエクスポートすることが望ましいかもしれません。これにより、エミッターインスタンスをドライバーに渡してイベントをリッスンするために使用できます。

start/emitter.ts
import { Emitter } from '@adonisjs/core/events'
export const emitter = new Emitter()
import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
import { Emitter } from '@adonisjs/core/events'
import { emitter } from '#start/emitter'
const emitter = new Emitter()
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
}
}

上記のコードは正常に動作します。ただし、今回はアプリケーションが必要とする依存関係を手動で構築しています。その結果、アプリケーションにはすべてを結びつけるための大量のボイラープレートコードが必要になります。

AdonisJSでは、最小限のボイラープレートコードを書き、依存関係の検索にIoCコンテナを使用することを目指しています。

コンフィグプロバイダを使用する場合

さて、config/hash.tsファイルを書き直して、今度はコンフィグプロバイダを使用しましょう。コンフィグプロバイダは、Applicationクラスのインスタンスを受け取り、コンテナを使用して依存関係を解決する関数のことです。

import { configProvider } from '@adonisjs/core'
import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
export default {
default: 'scrypt',
list: {
scrypt: configProvider.create(async (app) => {
const emitter = await app.container.make('emitter')
return () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
})
}
}

ハッシュサービスを使用すると、scryptドライバーのコンフィグプロバイダが依存関係を解決するために実行されます。その結果、コードの他の場所でハッシュサービスを使用するまで、emitterを参照しようとはしません。

コンフィグプロバイダは非同期なので、動的インポートを使用してScryptドライバーを遅延してインポートできます。

import { configProvider } from '@adonisjs/core'
import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
export default {
default: 'scrypt',
list: {
scrypt: configProvider.create(async (app) => {
const { Scrypt } = await import('@adonisjs/core/hash/drivers/scrypt')
const emitter = await app.container.make('emitter')
return () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
})
}
}

解決されたコンフィグにはどのようにアクセスしますか?

サービスから直接解決されたコンフィグには、次のようにしてアクセスできます。たとえば、ハッシュサービスの場合、次のようにして解決されたコンフィグへの参照を取得できます。

import hash from '@adonisjs/core/services/hash'
console.log(hash.config)