はじめに
Node.jsには、CommonJSのrequire()やES Modulesのimport、npmによるパッケージ管理など、成熟したモジュールシステムが用意されています。
一方で、複数のアプリケーションやライブラリを組み合わせて大規模なシステムを構築しようとすると、単なるモジュールの読み込みだけでは扱いにくい問題も出てきます。
例えば、次のような要求です。
- プロジェクト内のライブラリとnpmパッケージを統一的に扱いたい。
- アプリケーションを小さな部品に分割して再利用したい。
- ライブラリが提供する機能を動的に発見・登録したい。
- 非同期処理ごとに独立した実行コンテキストを管理したい。
- 複数のライブラリに分散したリソースを統合的に扱いたい。
こうした要求に対応するために開発しているのが、teaosです。
今回、Node.js向けのパッケージとしてnpmに公開しました。
- npm: https://www.npmjs.com/package/teaos
- GitHub: https://github.com/tea-noma/teaos
- 現在のバージョン: 0.1.1
- ライセンス: MIT
この記事では、teaosの基本的な設計思想と、現在公開されている機能を紹介します。
1. teaosとは
teaosは、Node.js上でローカルライブラリ、npmパッケージ、拡張コンポーネントを統一的に扱うための実行基盤です。
Node.jsの既存のモジュールシステムを置き換えるものではありません。既存のCommonJSやnpmを利用しながら、その上にアプリケーション構築のための機能を追加します。
現在のteaosは、主に次の機能を提供しています。
| 機能 | 概要 |
|---|---|
| Module Loader | ローカルライブラリとnpmパッケージの読み込み |
| Dynamic Module Loader | オプションを指定したモジュールの利用 |
| Extension System | 拡張コンポーネントの宣言と検出 |
| Event Listener | コンポーネント宣言などのイベント通知 |
| Context Management | 非同期処理単位の実行コンテキスト管理 |
| Dependency Management | ローカルライブラリとnpm依存関係の追跡 |
| Resource Management | 複数ライブラリのリソースの統合 |
| CLI | ライブラリの検出、作成、インストールなど |
これらは、それぞれ独立した機能でありながら、アプリケーションを部品化するための共通基盤として利用できます。
2. Node.jsのモジュールシステムとの関係
通常、Node.jsでは次のようにモジュールを読み込みます。
const fs = require('fs');
const express = require('express');
プロジェクト独自のライブラリであれば、相対パスで読み込むこともあります。
const mylib = require('./lib/mylib');
この方式はシンプルですが、プロジェクト内のライブラリが増えると、ディレクトリ構造とモジュールの参照方法が密接に結びつきます。
teaosでは、ローカルライブラリを@で始まる名前で識別します。
const teaos = require('teaos');
// プロジェクト内のローカルライブラリ
const mylib = teaos.require('@mylib');
// npmパッケージ
const lodash = teaos.require('lodash');
ローカルライブラリは、基本的に次の構造で配置します。
project/
├── package.json
├── node_modules/
│ └── teaos/
└── lib/
└── mylib/
└── index.js
この例では、@mylibがlib/mylibに対応します。
ここで重要なのは、ローカルライブラリとnpmパッケージを同じインターフェースで読み込めることです。
3. インストール
teaosはnpmからインストールできます。
ライブラリとしてインストール
npm install teaos
JavaScriptから利用する場合は、次のように読み込みます。
const teaos = require('teaos');
コマンドラインツールとしてインストール
npm install -g teaos
インストール後、次のコマンドでバージョンを確認できます。
teaos
ローカルライブラリの一覧を表示するには、次のコマンドを使います。
teaos ls
teaosには、このほかにもライブラリの作成、依存関係の確認、Extensionの検出などのコマンドが用意されています。
4. ローカルライブラリを作成する
teaosには、ローカルライブラリを作成するCLIがあります。
teaos create lib hello
このコマンドは、次のようなファイルを作成します。
lib/
└── hello/
├── index.js
└── teaos.json
index.jsに簡単な関数を定義してみます。
module.exports = {
hello(name) {
return `Hello, ${name}!`;
}
};
アプリケーション側から読み込みます。
const teaos = require('teaos');
const hello = teaos.require('@hello');
console.log(hello.hello('teaos'));
出力は次のようになります。
Hello, teaos!
この例では、アプリケーション側でlib/helloという物理パスを直接指定する必要がありません。
モジュール名と実際の配置を分離できることが、teaosの基本的な考え方の一つです。
5. requireとuseの違い
teaosには、モジュールを読み込むためのrequire()に加えて、use()というAPIがあります。
teaos.require(name);
teaos.use(name, options);
require()は、基本的にモジュールを読み込むためのインターフェースです。
一方、use()は、読み込んだモジュールのuse()メソッドが存在する場合に、オプションを渡して呼び出します。
例えば、次のようなライブラリを考えます。
// lib/greeting/index.js
exports.use = (options) => {
const prefix = options.prefix || 'Hello';
return {
greet(name) {
return `${prefix}, ${name}!`;
}
};
};
アプリケーションから利用します。
const teaos = require('teaos');
const greeting = teaos.use('@greeting', {
prefix: 'こんにちは'
});
console.log(greeting.greet('世界'));
出力:
こんにちは, 世界!
use()によって、ライブラリの初期化や構成をアプリケーション側から指定できます。
また、teaosのuse()には、ライブラリのExtensionを選択して読み込むためのresourcesオプションもあります。
Extensionについては、後続の記事で詳しく紹介します。
6. Extensionによるコンポーネントの宣言
teaosには、モジュールが提供するコンポーネントを宣言するためのdeclare()が用意されています。
teaos.declare(desc);
例えば、関数をコンポーネントとして宣言できます。
const teaos = require('teaos');
function add(a, b) {
return a + b;
}
teaos.declare({
type: 'function',
name: 'add',
func: add
});
宣言されたコンポーネントを受け取るには、on()を使用します。
const teaos = require('teaos');
teaos.on('declare', (desc) => {
console.log('Declared:', desc.type, desc.name);
});
teaos.declare({
type: 'function',
name: 'add',
func: (a, b) => a + b
});
出力:
Declared: function add
現在の実装では、declare()が登録済みのdeclareイベントリスナーを呼び出します。
この仕組みにより、コンポーネントの定義側と、それを収集・利用する側を分離できます。
例えば、あるライブラリが関数を宣言し、別のライブラリがその宣言を収集する、といった構成を考えられます。
ただし、declare()自体が永続的なコンポーネントレジストリを提供するわけではありません。宣言をどのように保持・利用するかは、リスナー側の実装に委ねられます。
teaosでは、この仕組みをさまざまなExtensionの共通基盤として利用することを想定しています。
7. 非同期処理のコンテキスト管理
Node.jsでは、非同期処理が並行して実行されるため、処理ごとに異なる状態を管理する必要があります。
例えば、Webアプリケーションでは、リクエストごとにユーザー情報や処理状態を保持する場合があります。
teaosでは、Node.jsのAsyncLocalStorageを利用して、実行コンテキストを管理できます。
const teaos = require('teaos');
const context = {
requestId: 'request-001',
userId: 123
};
teaos.run(context, async () => {
await Promise.resolve();
console.log(teaos.getContext());
});
出力:
{
requestId: 'request-001',
userId: 123
}
run()で指定したコンテキストは、対応する非同期処理の中でgetContext()から取得できます。
例えば、異なる処理に別々のコンテキストを渡せます。
const teaos = require('teaos');
async function task(id) {
await Promise.resolve();
console.log(
id,
teaos.getContext().requestId
);
}
Promise.all([
teaos.run(
{ requestId: 'A' },
() => task(1)
),
teaos.run(
{ requestId: 'B' },
() => task(2)
)
]);
出力例:
1 A
2 B
この仕組みは、リクエスト単位の処理、ログの追跡、アプリケーションごとの実行状態の管理などに利用できます。
なお、コンテキストの分離は、セキュリティ上のサンドボックス分離とは異なります。AsyncLocalStorageは非同期処理に状態を関連付けるための仕組みであり、それ自体がコードの権限を制限するわけではありません。
8. CLIによるモジュール管理
teaosには、開発中のモジュールやExtensionを調査するためのコマンドも用意されています。
例えば、次のようなコマンドです。
# ローカルライブラリ一覧
teaos ls
# ライブラリの詳細
teaos ls -l
# アプリケーション一覧
teaos ls apps
# Extensionの種類
teaos extension-types
# 検出されたExtension一覧
teaos extensions --all
# 依存関係一覧
teaos dependencies
# ローカルライブラリの依存関係
teaos dependencies-local
これらのコマンドは、ライブラリやExtensionが増えたときに、プロジェクト全体の構成を把握するために役立ちます。
また、teaos 0.1.1では、アプリケーションの依存関係を再帰的に解決するteaos installが拡張されています。
Gitリポジトリの取得方法についても、完全な履歴を取得するfull、浅い履歴を取得するshallow、Gitメタデータを残さないfilesというモードを選択できます。
teaos install apps/example --mode=shallow --user tea-noma
これは指定したリポジトリからアプリケーションを取得する場合のコマンド形式例です。exampleというアプリケーションが公開されていることを意味するものではありません。
9. 今後の展開
現在公開しているteaosは、Node.jsのモジュール管理とExtension機構を中心とした基盤です。
今後は、この基盤の上に、アプリケーションやデータリソースを扱う拡張機能を段階的に公開していく予定です。
例えば、次のような領域を考えています。
| 分野 | 予定している拡張 |
|---|---|
| Application | アプリケーションの構成と部品化 |
| API / Routes | APIやルーティングの定義 |
| Controllers / Models | アプリケーションロジックとデータモデル |
| Builder | 構成情報からのコード生成 |
| SQL | データアクセスの抽象化 |
| Resources | ファイル、ドキュメント、画像などの統合管理 |
| Language | 言語処理やデータ変換 |
これらは、現時点で全てが公開されているわけではありません。
今後、tea-os.comを通じてドキュメントや拡張パッケージを提供し、個々のライブラリを組み合わせてアプリケーションを構築できる環境を整備していく計画です。
特に重要視しているのは、Extensionの型とインターフェースを安定させることです。
共通インターフェースを維持しながら、個々のExtensionを独立して開発・更新・交換できる構造を目指しています。
10. まとめ
今回は、npmに公開したteaosの基本的な機能を紹介しました。
teaosは、Node.jsのモジュールシステムを活用しながら、次のような機能を追加するためのパッケージです。
- ローカルライブラリとnpmパッケージの統一的な読み込み
-
use()による動的なモジュール利用 -
declare()とon()によるコンポーネント宣言 -
AsyncLocalStorageを利用した実行コンテキスト管理 - CLIによるライブラリやExtensionの検出・管理
現段階では小さな基盤ですが、今後、ExtensionやResourceの仕組みを段階的に公開し、アプリケーションの部品化と再利用を進めていく予定です。
次回は、teaosの基本API ― require / use / run / getContextをテーマに、モジュールの読み込みと実行コンテキストの仕組みをもう少し詳しく解説します。
関連リンク
teaos 技術シリーズ 第1回
対象バージョン:teaos 0.1.1