May 2016

Volume 31 Number 5

働くプログラマ - MEAN あれこれ: Edge.js の使用

Ted Neward | May 2016

Ted Neward「MEANers」の皆さん、お帰りなさい。前回は、コツコツと構築しているソフトウェア スタックに MongooseJS ライブラリを追加したことで、構造のなかった JavaScript、Node、および MongoDB 環境が少々構造化されました。これにより、Node/Express ミドルウェアが受け取って保存していたさまざまなコレクションの周辺に、いくらか「スキーマ」が追加されました。これは、よくある人為的ミス (正しいフィールド名「firstName」ではなく「fristName」を検索するなど) を回避するのに役立ちます。そして何と言っても、MongooseJS は完全にコード側なので、少なくともデータベースに関して、事実上 2 つの世界の「いいとこ取り」、つまり、(リファクタリングを容易にする) データベースの「スキーマレス」な性質と、(スペルミスで混乱が起こる可能性を非常に少なくする) コードの「スキーマフル」な性質を両方利用できます。

とは言え個人的には、Microsoft .NET Framework が恋しいというのも正直な話です。具体的には、.NET エコシステムの持つ非常に優れた点を取り入れたいと思っています。と言うのも、Microsoft Azure クラウドでは多くの組織が .NET「スタック」に少額の (または多額の) 投資をしようとしますが、Azure での実行を話題にするときに JavaScript ばかりを語るのでは、.NET に関するものすべてが使用できないように聞こえ、少し場違いのように思えます (1 か所のデータセンター内で運用している場合はばからしく思える、なんらかの HTTP スタイルの要求を長時間運用している場合を除きます)。

さいわい、.NET にはエッジの効いた機能があります。それが Edge.js です。

Edge.js

多くの意味で Edge.js は本当に独特なプロジェクトです。最も特筆すべきは、.NET と Node.js の間の「プラットフォームの溝」に直接対処しようとすることです。Edge.js は bit.ly/1W7xJmo (英語) でホストされていて、非常にコードに優しい方法で、各プラットフォームが相互に使えるよう慎重にはからいます。

たとえば、.NET 関数を呼び出す Node.js コード サンプルは次のようになります。

var edge = require('edge');
var helloWorld = edge.func(function () {/*
  async (input) => {
    return ".NET Welcomes " + input.ToString();
  }
*/});
helloWorld('JavaScript', function (error, result) {
  if (error) throw error;
  console.log(result);
});

これはプログラムとしては難しくありません。関数リテラルを edge.func メソッドに渡し、その関数リテラルの中に .NET コードを含め、コメント本文として呼び出しています。

そうです、コメントです。次の点を理解していれば、これはさほど奇妙なことではありません。

  • 文字通りの C# 構文にはできません。さもないと、Node インタープリターはこれを正当なプログラム構文として認識しません (というのも結局、Node インタープリターは JavaScript インタープリターであり、C# インタープリターではないからです)。
  • コンパイル済みのプログラムとは異なり、インタープリターは、コンパイラが生成したものだけでなく、定義されているあらゆるソース コードの本文全体にアクセスすることができます。

ちなみにこれは C# に限ったことではありません。Edge.js プロジェクトでは、F#、Windows PowerShell、Python、Lisp など、いくつかの言語を Edge 呼び出しの「ターゲット」として使用することができます。このために、各言語の .NET 実装を使用します。お気に入りはもちろん、F# です。

var edge = require('edge');
var helloFs = edge.func('fs', function () {/*
  fun input -> async {
    return "F# welcomes " + input.ToString()
  }
*/});
helloFs('Node.js', function (error, result) {
  if (error) throw error;
  console.log(result);
});

主な違いは、関数リテラルの前に付加されている引数です。その関数コメントでどの言語が渡されているかを示しています。

ここで理解すべき重要な点は、その関数の本文が (C# でも F# でも)「Func<object, Task<object>>」という特定の .NET 型のシグネチャになっていることです。Node は、Node.js のメイン イベント ループがブロックされないように、シーケンシャル実行にリダイレクトするコールバックを採用しているため、こうした非同期性が必要です。

Edge.js は、コンパイル済みの .NET DLL でこれらの関数をかなり簡単に呼び出せるようになっています。したがって、たとえば、コンパイル済みの .NET コード アセンブリを呼び出す場合、次のようにアセンブリ名、型名、メソッド名が「func」呼び出しの一部として提供されるため、Edge.js はそのアセンブリ名が有効である限り呼び出すことができます。

var helloDll = edge.func({
  assemblyFile: "Echo.dll",
  typeName: "Example.Greetings",
  methodName: "Greet"
});

Greet の型シグネチャが図 1 のような「Func<object, Task<object>>」の場合、Node.js は、他の例で示しているように (入力引数と関数コールバックを渡す) 同じ呼び出しパターンを使用して呼び出すことができます。

図 1 Edge.js と互換性のある .NET エンドポイント

using System;
using System.Threading.Tasks;
namespace Example
{
  public class Greetings
  {
    public async Task<object> Greet(object input)
    {
      string message = (string)input;
      return String.Format("On {0}, you said {1}",
        System.DateTime.Now,
        Message);
    }
  }
}

別の方法で .NET コードから Node.js パッケージを呼び出すようにもできますが、今回の目的はサーバー側で Node.js を動かすことなので、ここでは扱いません。ご興味があればぜひ調べてみてください (ちなみに、Edge.js 関連のものはすべて、Mac よりも Windows の方がずっと簡単に操作することができます。本稿執筆中に私物の Mac で作業を試みましたが、全体的に見て明らかに時間がかかったので、Node.js 関連の開発のこの部分では確実に、Windows のエクスペリエンスがMac を凌ぎます)。

複雑な内容に入る前に、簡単な「Hello World」をお見せします。

Hello, Edge

まずは重要なことから実行します。すべては (今回選んだデプロイ環境の) Microsoft Azure で動作しなければならないので、Azure でコミットされるときに package.json マニフェストで追跡されるように「npm install --save edge」を実行します。次に、app.js コードに helloWorld 関数を追加します。そして、そこに "GET" して helloWorld を HTTP 経由で取得するために、簡単なエンドポイントをセットアップします (図 2 参照)。もちろん、GET を msdn-mean.azureweb­sites.net/edgehello に送信すると、次の答えが返ってきます。

{"message":".NET Welcomes Node, JavaScript, and Express"}

図 2 helloWorld 関数の追加

var helloWorld = edge.func(function () {/*
    async (input) => {
        return ".NET Welcomes " + input.ToString();
    }
*/});
var edgehello = function(req, res) {
  helloWorld('Node, JavaScript, and Express', function (err, result) {
    if (err) res.status(500).jsonp(err);
    else res.status(200).jsonp( { message: result } );
  });
};
// ...
app.get('/edgehello', edgehello);

Hello, SQL Server

.NET 開発者と MEAN スタックをテーマに話をすると、いつも受ける質問の 1 つが MongoDB に関することです。特に Azure で実行しているとき、ほとんどのユーザーは SQL Server を使用しないという選択肢を好みません。もちろん、Node.js コミュニティはいくつかリレーショナル データベース アクセス API を構築していて、SQL Server はそのいずれから見ても単なる TDS 接続でしかありませんが、Edge.js は実のところ、この問題に対してかなり魅力的なソリューションを提供します (次のように「npm install --save edge-sql」を実行して Edge-SQL パッケージを取得します)。

var edge = require('edge');
var getTop10Products = edge.func('sql', function () {/*
  select top 10 * from Products
*/});
getTop10Products(null, function (error, result) {
  if (error) throw error;
  console.log(result);
  console.log(result[0].ProductName);
  console.log(result[1].ReorderLevel);
});

このコードは、EDGE_SQL_CONNECTION_STRING という環境変数が Azure 環境にあり、(この場合 Azure で実行されている SQL Server インスタンスをおそらく指している) 適切な SQL Server 接続文字列がこの環境変数に設定されていると見なします。

これは正直なところ、今まで見てきたどのようなものよりもシンプルと言えるでしょう。確かに、これがすぐに Entity Framework を置き換えることはないでしょう。ですが、(厳密にスキーマ化されたリレーショナル データを保存するために SQL Server、そしてスキーマレスの JSON データを保存するために MongoDB を使用する、「多言語」つまり複数ストレージのアプローチの一環として) SQL Server インスタンスにすばやくアクセスする場合は、たいへん洗練された方法です。

Edge.js が便利な理由

ほとんどの開発者は、複数の言語に精通することを求めるソリューションに懐疑的です。というわけで、そのようなソリューションをいつどのように使用するのか、もう少し詳しく解説することにします。

レガシー コードのサポートが必要ないほとんどのグリーンフィールド プロジェクトでは、単一の言語かプラットフォームを一貫して使用するのが原則です。たとえば .NET に固執して Web API などを使用する場合もあれば、とことん Node.js を使用し、目的を果たすために Node.js のさまざまなライブラリやパッケージに依存する場合もあるでしょう。結局、.NET で実行するあらゆることにおいて、対応するパッケージがおそらく npm パッケージ リポジトリに存在するということです。ただし、このアプローチにはいくつかの注意点があります。

まず第一に、.NET エコシステムには「長い歴史」という利点があるので、いくつかのパッケージは実証済みで信頼が置けます。一方で npm パッケージの多くには依然として、非常に心もとないバージョン番号が付いています。たとえば 0 から始まるバージョン番号のものを信頼するのはマネージャーにとって難しいことです。

そして第二に、問題によっては、特定のプログラミング手法や環境が適していることがあります。この典型例が F# 言語で、数学的背景を持つ開発者にとって使い勝手が良いことが多く、特定の種類のコードが記述しやすくなる場合があります。その好例が、数年前に (Tropo クラウドを使用してクラウドで SMS を使用する方法を解説したシリーズの一環として) Feliza ライブラリを記述したときのことです。C# で記述することもできましたが、F# のパターン マッチングと「アクティブ パターン」のおかげで、C# よりもずっと簡単に記述できました。JavaScript でも同じことがいえます (こちらの方がさらに簡単になるかもしれません)。

最後に、これが最も重要なことですが、コードの実行環境は本質的に特定のプラットフォームに適している場合があります。たとえば、多くの組織が Azure でアプリケーションをホストしていますが、認証と承認のベースとして Active Directory を使用しています。その結果、記述されたのが .NET でも Node.js でも、新しいアプリケーションには Active Directory を使用し続けることが求められます。一般的に、.NET 環境から Active Directory にアクセスするのが最もシンプルかつ容易なので、Edge.js ライブラリはいわば、Active Directory へのアクセスをずっと簡単にする便利な「抜け道」を用意すると言えます。

まとめ

今回はいつもより少し簡単でしたが、その理由は主に、Edge.js ライブラリが .NET 環境との相互運用にかかる労力を大きく軽減するためです。そして、Edge.js は Azure の「MEANers」に多くの新しい選択肢を提供します。ツール、ライブラリ、パッケージの完全なエコシステムに、いまや 1 つのみならず 2 つアクセスできるようになりました。

ちなみに、コラム 1 本分にも相当する、今回とは反対方向のトピックがあります。多くの場合、特定の種類のアプリケーションは、npm リポジトリにあるさまざまなパッケージを使用すると、作成が大幅に容易になるため、従来からの .NET 開発者にも Edge.js が広がり始めています。また最近、オープン ソースで公開された Chakra (ドロップイン プラグインとして Node 環境に「プラグイン」できるマイクロソフトの JavaScript コア) により、標準 .NET アプリケーションの一部として JavaScript を使用するチャンスがさらに増えました。アプリケーションの中心で JavaScript インタープリターをホストすることも可能です。これには興味深い意味があるので、ぜひ調べてみてください。

完全にサーバーに移行する前に、数点触れておきたいことがありますが、スペースが尽きてしまいました。ひとまずは、コーディングを楽しんでください。


Ted Neward は、シアトルを拠点に活躍している、ポリテクノロジーに関するコンサルタント、講演者、および指導者です。これまでに 100 本を超える記事を執筆している Ted は、F# MVP および INETA 講演者で、さまざまな書籍を執筆および共同執筆しています。仕事への協力を依頼する場合、連絡先は ted@tedneward.com (英語のみ) です。また、tedneward.com (英語) でブログを公開しています。

この記事のレビューに協力してくれた技術スタッフの Shawn Wildermuth に心より感謝いたします。