JavaScriptフレームワーク調査

JavaScriptのモダンフレームワークでどれを選べば良いのか調査した内容になります。

AngularJS

googleによっって開発されているMVVMフレームワーク 現在Angular4まで出ている。出た当初は仮想DOMを使っているReactなどに比べると遅いと言われていたけど、現在はReactと比べて特に遅いというふうではないらしい。

テンプレート

HTMLテンプレートを作成しデータとtwo-wayバインディングさせる。ReactやVueなどの仮想DOMを使うフレームワークの場合はone-wayでデータをバインディングさせるが、Angularの場合は仮想DOMを使っていないためかtwo-wayバインディングで表示用のデータを手軽に操作することができる。 JavaScript内部にhtmlを記述してデータのバインドやテンプレートの読み込みをするなどAngularフレームワーク独特の学習は必要になりそう。

DI

作成したモジュールをサービスとして別のモジュールで利用する(DI)ことができる。コンポーネント間でのデータのやり取りでもサービスを使うらしい。

テスト

Angularのテンプレート構文はJSXのように静的チェックが行えないためテストツールが必須になっているとのことらしい。その分公式の方がどのテストフレームワークを使えば良いか説明していて、それに合わせて作っていたら問題なさそう。

所感

フルスタックフレームワークな分覚えることは多いけど、公式のサポートが充実してそうなので特に困ると言ったことはなさそうなきがする。他のReactやVueなどのフレームワークに比べるとHttpリクエストなどフレームワーク側でのサポートが広い範囲で行われるので安心して使うことはできそう。業務でSPAを扱うと言ったことがあればAngularが良さそうなきがする。

React

React自体はMVVMのViewの部分だけを扱うフレームワークでシンプルと言われているけど、Facebookが提唱するFluxやReduxのアーキテクチャに合わせて実装しようとするのであればその分書くコードは増えて複雑になる。React自体がシンプルな分どう使うかは開発者自身に委ねられている部分が多い気がする。学習コストについてReact自信を覚えるのは大変ではないけどどう設計するかで時間がかかるかもしれない。

テンプレート

テンプレートにはJSXを使用している。JavaScriptの中に直接HTMLを吐きだす関数が書かれているため、デザイナーでない人が見るのは大変だと思われる。JSX自体はReactを使っている開発者自体にも嫌われているので、今後の改善が望まれる。

テスト

公式ではJesとかenzymeが進められていて、Jsetの方が簡単に扱えそうな感じがした。   enzymeのを使用する場合は別でモック用のモジュールを入れている記事がいくつか見受けられる。

Redux

Reduxはだいたいこんな感じだと思う。 アプリケーション全体で管理するstateを変更する場合はマウスクリックなどのイベント後に、状態変更のためのアクションを実行する。 アクションによりデータが作成されたら変更を反映するため、ディスパッチといってstateを管理するstoreにデータを送る。 reduxではここでstateの変更をすぐに変更するのではなくmidlewareの処理を実行する。 midlewareはログの出力やサーバへのリクエスト投げたりするのに使われている気がする。
midlewareが増えると全体的に遅くなるという問題はあるようです。 midlewareの一連の処理が終わったらreducerによりアプリケーションの状態を表すstateが変更され、 そして仮想DOMが変更され描画に反映さる。

所感

Reduxの実装方法であればデータを厳密に扱うことができるので、例えば一つのデータに対していろんな見せ方があったり、いろんなところから変更したりという複雑さがある場合はReactを使った方がよさそうな気がした。

Vue

公式サイトよりVueの概要は以下のようになっています。

Vue.js (発音は / v j u ː /、view と同様) はインタラクティブな Web インタフェースを構築するためのライブラリです。Vue.js のゴールは、
できる限りシンプルな API でリアクティブデータバインディング と 構成可能な View コンポーネントを提供することです。

Vue.js 自体は本格的なフレームワークではありません、Vue.js は View レイヤーだけに焦点を当てています。したがって、Vue.js
 のいいところだけをピックアップしたり、Vue.js を他のライブラリや既存のプロジェクトに統合することはとても簡単です。一方、Vue.js
 を適切なツールとサポートするライブラリによる組み合わせで使用する場合、Vue.js は完全に洗練されたシングルページアプリケーションを提供することができます。

あなたが経験豊富なフロントエンド開発者で、 Vue.js を他のライブラリ/フレームワークと比較したい場合、他のフレームワークとの比較を
チェックしてください。Vue.js で大規模アプリケーションを扱う方法に興味がある場合は、大規模アプリケーションの構築をチェックしてください。

AngularとReactに比べると学習コストは低く、というか多分Vue自体がjQueryの代替として考えられている気がしてその分低いと認識されている気がする。

テンプレート

htmlのテンプレート構文を利用していてバインドしているデータに変更があったら最小限のDOM再描画を行っている。 テンプレート構文を利用する点がPolymerと似ていたりして、JSXとに比べて普段htmlを 書いている人からしたら親しみやすいのかと思う。オプションでJSXを利用することもできる。

ReactとVueは似ているけど描画のロジックは根本的に違っているようで、Reactの方は際の DOM がどのような状態にするためにメモリ内の表現で仮想 DOM を活用し、状態を変更するとき、React は仮想 DOM の完全な再レンダリングを行い、その差分を求めて、そして実際の DOM にパッチする。 仮想 DOM の代わりに、Vue.js はテンプレートとして実在する DOM を使用し、データバインディングに対して実在するノードに参照を保ちます。そのためかVue.jsは性能のチューニングをほとんど必要としないらしい。

テスト

公式はKarmaを使うことを勧めている。

所感

jQueryの代替品を探しているのであればこれがベストなきがする。

結論

一般的な業務でSPAを扱うということがあるのであれば公式のサポートが手厚いAngularがよさそうに思います。jQueryの代替品を探しているレベルであればVueで手軽に実装するのが良さそう。データ操作周りで複雑なことをして見せたいページを作りたいのであればReactが良さそうな感じでしょうか。

f:id:steavevaivai:20170610124847p:plain

DDD概要

社内の勉強会に備えてのメモ

DDDとは何か?

エリック・エヴァンスがソフトウェアのドメインモデリングと設計についてオブジェクトコミュニティの底流として現れた哲学をドメイン駆動設計と呼称し書籍にまとめた。
DDD(Domain-Driven Design) とはどのように設計していくかという理論、考え方、概念を指している。
具体的な設計・開発方法を指して言って入るわけではないのでわかりづらい点も多々あるかもしれない。

エヴァンス本の参考文献からもDDDは以下の書籍によって支えられて入ることがわかる
 ・XPエクストリープ プログラミング入門
 ・テスト駆動開発入門
 ・アナリシスパターン
 ・実践UML
 ・オブジェクト指向入門
他にもたくさんあって、DDDはこれらの書籍によって支えられている。

DDD(エヴァンス本)の目次

エヴァンス本の目次一覧をまとめてみる

第1部 ドメインモデルを機能させる

 ・第1章 知識をかみくだく
 ・第2章 コミュニケーションと言語の使い方
 ・第3章 モデルと実装を結びつける
第1章では設計者とドメインエキスパートでのコミュニケーションによりどうやって深いモデルを見つけていくかについて書かれている。そのためにもドキュメントを作ったり言葉を統一することによりコミュニケーションを取る方法を提示している。第3章では知識をかみくだいてえたモデルをコーディングに落とし込む手法としてモデル駆動設計に触れている。

第2部 モデル駆動設計の構成要素

 ・第4章 ドメインを隔離する
 ・第5章 ソフトウェアで表現されたモデル
 ・第6章 ドメインオブジェクトのライフサイクル
 ・第7章 言語を使用する:応用例
第2部ではモデルを実装に落とし込むモデル駆動設計について述べている。ドメインの設計をソフトウェアシステムにおけるその他の関心ごとから分離することで設計とモデルとのつながりを明確にする目的がある。そのためにも"第4章ドメインを隔離する"ではUI層、アプリケーション層、ドメイン層、インフラストラクチャ層のレイヤ化アーキテクチャについて説明している。"第5章ソフトウェアで表現されたモデル"ではモデルの実装部分であるドメイン層の構成要素のエンティティ、値オブジェクト、サービスについて説明している。それから2つのモデルを適切に分割(モジュール化)することによるメリットを述べている。"第6章ドメインオブジェクトのライフサイクル"ではエンティティ、値オブジェクトの集約について述べている。例えば自動車のドメインであったら集約ルートが自動車でそれに紐づいてタイヤエンティティ、車輪エンティティ、位置値オブジェクトが存在する。集約全体のオブジェクト精製方法についてファクトリパターンの説明がある。集約のルートがオブジェクトの永続化と永続化されたオブジェクトの検索に使用するリポジトリにも触れている。

第2部は実装寄りの話でここを理解できていればDDDっぽい実装ができるようになりそう。

第3部 より深い洞察へ向かうリファクタリング

 ・第8章 ブレイクスルー
 ・第9章 暗黙的な概念を明示する
 ・第10章 しなやかな設計
 ・第11章 アナリシスパターンを適用する
 ・第12章 デザインパターンをモデルに関係づける
 ・第13章 より深い洞察へ向かうリファクタリング
第2部ではモデルを実装を一致させる基礎の方法を学び、第3部ではドメインについてさらに深いモデルを開発するために必要不可欠となるリファクタリングを学ぶ。"第8章ブレイクスルー"は最初に思い描いたモデルで開発を進めた後、モデル自体は悪くなさそうであっても不具合が生じることがあって、そういうのは普段からの小さいリファクタリングによりモデルを明確化することである時問題が明確化して解決の糸口になるという話。"第9章暗黙的な概念を明示的にする"ではブレイクスルーを引き起こすための日々のリファクタリングとしてそれまで気づいていなか明白でない概念を見つける方法について書いている。"第10章しなやかな設計"では変更に強いしなやか設計はどうすれば行えるかについて書いてある。第11章と第12章ではアナリシスパターンデザインパターンの利用方法について書いてある。"第13章より深い洞察へ向かうリファクタリング"ではどこからリファクタリングを始めるかについて書いてある。

第4部 戦略的設計

 ・第14章 モデルの生合成を維持する
 ・第15章 蒸留
 ・第16章 大規模な構造
 ・第17章 戦略をまとめ上げる
第4部ではモデルの生合成を維持していくための戦略的設計について述べている。"第14章モデルの生合成を維持する"では、コンテキストマップを描きコンテキストを境界づけておき、コンテキスト間の関係を明示的にしておく。"第15章蒸留"ではコンテキストマップを描いてコンテキスト間を境界づける際に各ドメインの役割となるコアドメインや汎用サブドメインなどの種類を学ぶ。"第16章大規模な構造"では巨大なシステムに包括的な原則を持たせることで設計全体にまたがるパターンにおいてどのような役割を果たすかという観点を分かりやすくする方法を学ぶ。

全17章でソフトウェアの上流部分からリファクタリング、成長までをまとめた長編になって入るのがわかる。DDD自体は具体的な実装についてあまり触れられていないので、実装にどう活かすかを学びたいのであれば実践ドメイン駆動設計を読んだり、サンプルのプロジェクトを調べるのが良さそう

JJUG_CCC 2017 Spring

2017/05/20(土)に行われたJJUG(ジェイジャグ)に参加してきました。JJUGは毎年2回春と秋に行われていて、今回で20回目とのことでした。1コマ45分のセッションが複数のルームで同時に行われ参加者はどれに出るか自由に選ぶというものになっています。参加者は前回の約700名からさらに増えて1000名近くいたようでとにかくたくさん人がいました。人気のセッションはルームに入る前に行列ができてすぐに満席になっていました。会場の整備や入場規制、タイムキーパーなどで対応いただいた運営の方は忙しい中ありがとうございました。

エンプラ開発におけるレガシーアプリケーションの巻き取りとモジュール分割の戦い

https://www.slideshare.net/KazuhiroWada/2017spring-jjug-cccf2-76144077
既存のレガシーアプリケーションを巻き取ることになったベンダーさんがどうやって品質の改善に取り組んだのかという話でした。元々struts1で作られたアプリがあってそれと連携するアプリを開発して2つのアプリをearで固めてデプロイするということをやっていたらしいです。元々のアプリをアプリ1とし、新しく作るアプリをアプリ2としたら、連携するにあたりアプリ1側で持っていた認可判定の処理をアプリ2に持って行きspring security OAuth2のOAuth2 Providerという機能を利用してアプリ1から認可判定の処理を行うときもアプリ2側に作ったロジックを利用するとのことでした。目標が決まった後はクリスさんのレガシーソフトウェア改善ガイドのような手順で進めていたようです。 Gitリポジトリの管理にはBitbucketを情報共有やバグトラッキングにはAtlassianのConfluenceとJIRAを利用し継続的インテグレーションにはBambooを利用したとのことです。ビルドツールがgradleならGradleWrapperを利用することもできるのでjenkinsでも良さそうな気がしましたが、Bambooは触ったことがないからわからないです。リファクタリング自体で工数が取れることはないので、機能追加とかのタイミングの修正タイミングで少しづつ変えていくしかないのかなと思いました。作業を効率化するにも普段から意識すべきことが聞けてよかったと思います。

Scala機械学習基盤PredictionIOとSparkによるレコメンドシステム

https://speakerdeck.com/takahiro/building-a-recommendation-engine-with-spark-and-apache-predictionio
PredictionIOのコミッタの方が発表していました。PredictionIOはGithubScalaOSSでStar数がApacheSparkについで2番目とのことらしいです。SparkやPredictionIOのstar数が多いあたりScalaにはデータの集計や分析の需要が多いのでしょうか。開発元が2016年にSalesForceに買収されたニュースで知った人もいるかと思います。PredectionIOは機械学習に必要となる機能を体型的に備えているため、Datastoreや集計、機械学習APIサーバとだいぶ機能が豊富な印象です。
発表では求人広告のデータを学習させてオススメの求人を見つけれるようにするための方法について話していました。手順については学習データを用意して前処理を行いモデルを学習しテストを行い、交差検証で一番よかったモデルを採用するというものです。PredictonIO導入前はElasticsearch連携用にインデックスを張ったり学習結果をバルクファイルで出力したり実行フローのスクリプトが属人化して最初に作った人もよくわからない状態であったりなどしたらしいですが、PredictionIO導入により改善が行えたようです。Sparkが流行った流れでPredictionIOにもブームが来るんじゃないでしょうか。

javascriptでオブジェクトのプロパティを再起的にマージしてみる

以外と調べても良いのが見つからなかったから自分で作ってみた時の備忘録
配列とかの順番で情報の持ち方が決まっているのであればこれで良いのかも

function mergeProperty(obj1, obj2){
    if(typeof obj2 === "string" ){
        return;
    }
    if(obj2.length !== void 0 && obj2.length > 0){
        for(var i = 0; i < obj2.length; i++ ){
            if(obj1.length <= i ){
                obj1.push(obj2[i]);
            }else{
                mergeProperty(obj1[i], obj2[i]);
            }
        }
    }else{
        for(prop in obj2){
            if(obj1.hasOwnProperty(prop)){
                mergeProperty(obj1[prop], obj2[prop]);
            }else{
                obj1[prop] = obj2[prop];
            }
        }
    }
};


var obj1 = {
    prop1: 'obj1-prop1',
    prop2: [{prop2_1: 'obj1-prop21', prop2_2: 'obj1-prop22'}, {a: 'obj1-prop2_a'}] ,
    prop3: 'obj1-prop3'
};

var obj2 = {
    prop1: 'obj2-prop1',
    prop2: [{prop2_1: 'obj2-prop21', prop2_2: 'obj2-prop22'}, {b: 'obj2-prop2_b'}, { 3: 'obj2-prop2_3'}] ,
    prop_d: 'obj2-prop_d',
    prop_e: ['a', 'b']
};

mergeProperty(obj1, obj2);
JSON.stringify(obj1);
"{"prop1":"obj1-prop1","prop2":[{"prop2_1":"obj1-prop21","prop2_2":"obj1-prop22"},{"a":"obj1-prop2_a","b":"obj2-prop2_b"},{"3":"obj2-prop2_3"}],"prop3":"obj1-prop3","prop_d":"obj2-prop_d","prop_e":["a","b"]}"

こんな感じでobj1にないプロパティがobj2にある場合にのみマージするようになります。

bat内での別のbatの呼び出し方について

bat内で複数の別のbatを呼び出すようにしていた時、1個目のbatが終了したら残りのbatを呼び出さずに処理が終了していた。調べてみたらcallまたはstartで呼び出すのが正しいらしい。
callの場合は呼び出したbatの処理が終了するのを待ってから次の処理を行っているのに対して、startでは処理を待たずに次の処理に移るという違いがあるっぽい。実際試してみたらstartの方は呼び出す時に別のコマンドプロンプトが開かれて処理を行い、元のコマンドプロンプトではそのまま次の処理に移っていた。

TypescriptでReactのハンズオンプロジェクトを作ってみた

以前ES6でReactのハンズオンを作成し、今回はTypescriptが触ってみたかったのでTypescriptでReactのハンズオンプロジェクトを作ってみました。

github.com


まだまだTypescriptに慣れていないので型を指定するところではanyでやり過ごす部分が多々あったので、この辺りは触りながら都度修正していけたらと思います。

TypescriptでReactのプロジェクトを作ってみた

公式の手順に従いプロジェクト作成

公式の手順に従ってTypescriptでReactのプロジェクトを作成してみたいと思います。

まずプロジェクトルートのディレクトリをさくせしnpm initを実行します。
それからwebpackをグローバルインストールします。

npm install -g webpack

次に必要になるモジュールをインストールします。

npm install --save react react-dom @types/react @types/react-dom    
npm install --save-dev typescript awesome-typescript-loader source-map-loader

次にプロジェクトルートに以下の"tsconfig.json"を作成します。

 {
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "module": "commonjs",
        "target": "es5",
        "jsx": "react"
    },
    "include": [
        "./src/**/*"
    ]
}

それから"src/components/Hello.tsx"を作成します。

import * as React from "react";

export interface HelloProps { compiler: string; framework: string; }

export const Hello = (props: HelloProps) => <h1>Hello from {props.compiler} and {props.framework}!</h1>;

次に"src/index.tsx"を作成します。

import * as React from "react";
import * as ReactDOM from "react-dom";

import { Hello } from "./components/Hello";

ReactDOM.render(
    <Hello compiler="TypeScript" framework="React" />,
    document.getElementById("example")
);

次に"index.html"を作成します。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
    </head>
    <body>
        <div id="example"></div>

        <!-- Dependencies -->
        <script src="./node_modules/react/dist/react.js"></script>
        <script src="./node_modules/react-dom/dist/react-dom.js"></script>

        <!-- Main -->
        <script src="./dist/bundle.js"></script>
    </body>
</html>

ビルドの設定に必要になる"webpack.config.js"を作成します。

module.exports = {
    entry: "./src/index.tsx",
    output: {
        filename: "bundle.js",
        path: __dirname + "/dist"
    },

    // Enable sourcemaps for debugging webpack's output.
    devtool: "source-map",

    resolve: {
        // Add '.ts' and '.tsx' as resolvable extensions.
        extensions: [".ts", ".tsx", ".js", ".json"]
    },

    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
            { test: /\.tsx?$/, loader: "awesome-typescript-loader" },

            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
        ]
    },

    // When importing a module whose path matches one of the following, just
    // assume a corresponding global variable exists and use that instead.
    // This is important because it allows us to avoid bundling all of our
    // dependencies, which allows browsers to cache those libraries between builds.
    externals: {
        "react": "React",
        "react-dom": "ReactDOM",
    }
};    

ここまで出来たらプロジェクトルートで以下のwebpackコマンドを実行したらdistにビルドしたものが出力されます。

webpack

動きを確認する場合はプロジェクトルートで以下のコマンドを実行しプロジェクトのルートをドキュメントルートとした上でサーバを起動した上で"http://8000/index.html"にアクセスすることで確認できます。

python -m SimpleHTTPServer

vue-cliからプロジェクト作成

vue-cliのインストール

npm install -g vue-cli

プロジェクト作成

vue init webpack react_typescript_starter_project  
cd react_typescript_starter_project  
npm install  

一旦起動してみる

プロジェクト作成直後はvueのプロジェクトになっておりますが、とりあえず以下のコマンドでwebpackを起動して動作を確認してみたいと思います。

npm run dev    

上記コマンド実行後にブラウザが開いてvue-jsのアイコンが表示された画面が表示されたかと思います。

vue-cliで作成したプロジェクトをreact化する

それではreactで開発できるようにしていきたいと思います。まずは必要なモジュールをインストールします

npm install --save react react-dom @types/react @types/react-dom    
npm install --save react-redux redux @types/react-redux @types/redux    
npm install --save-dev typescript awesome-typescript-loader source-map-loader    

tsconfig.jsonを作ります。

{
   "compilerOptions": {
       "outDir": "./dist/",
       "sourceMap": true,
       "noImplicitAny": true,
       "module": "commonjs",
       "target": "es5",
       "jsx": "react"
   },
   "include": [
       "./src/**/*"
   ]
}

“build/webpack.base.conf.js"にTypescriptの情報も追加します。

var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    //app: './src/main.js'
    app: './src/index.tsx'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src')
    }
  },
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
      {
        test: /\.tsx?$/,
        loader: "awesome-typescript-loader"
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
      {
        enforce: "pre",
        test: /\.js$/,
        loader: "source-map-loader"
      },/*
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },*/
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}

それから"src/components/Hello.tsx"を以下の内容で作成します。

import * as React from "react";

export interface HelloProps { compiler: string; framework: string; }

export const Hello = (props: HelloProps) => <h1>Hello from {props.compiler} and {props.framework}!!</h1>;

次に"src/index.tsx"を作成します。

import * as React from "react";
import * as ReactDOM from "react-dom";

import { Hello } from "./components/Hello";

ReactDOM.render(
    <Hello compiler="TypeScript" framework="React" />,
    document.getElementById("example")
);

最後に"index.html"を以下の内容で作成したら完成です。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
    </head>
    <body>
        <div id="example"></div>
    </body>
</html>

これで"npm run dev"を実行すると実際に動いているのが確認できると思います。それから"npm run build"を行った後にdistディレクトリで"python -m SimpleHTTPServer"を実行してみるとビルドがうまくいくことも確認できます。webpackの設定周り詳しくなくてもこれならなんとかなりそうな気がします。

今回vue-cliで作成したプロジェクトは以下にあげておきました。 https://github.com/teruuuuuu/react_typescript_starter_project