クリーンアーキテクチャの書籍を読んだのでAPIサーバを実装してみた

クリーン<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%AD%A5%C6%A5%AF%A5%C1%A5%E3">アーキテクチャ</a>の書籍を読んだので<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>サーバを実装してみた


クリーンアーキテクチャの書籍を読んだのでAPIサーバを実装してみた



はじめに

クリーンアーキテクチャの書籍を読んだので、実際にクリーンアーキテクチャの考え方を採用したREST APIをGO言語で実装してみた。

↓↓↓↓ソースコード↓↓↓↓

https://github.com/yoshinorihisakawa/sample-api-hoop/tree/develop

この記事ではクリーンアーキテクチャの説明というよりかは、実装ベースの実践的な内容にしている。


対象読者

・クリーンアーキテクチャで実装されたソースコードを理解したい人

・クリーンアーキテクチャの右下の図がよくわからない人

アーキテクチャについて勉強を始めた初心者


クリーンアーキテクチャとは?

クリーンアーキテクチャとは、8th Light, Inc.のブログ記事で提案されている。

一言で言うと、依存関係をコントロールし持続可能なソフトウェアを実現するための体系的な手法である。

※ DIやDIP、依存については知っていないと読みづらいかと思う。

 知らなければ、以下の記事を一読しておくと理解が深まると思う。

・DI、DIP(https://qiita.com/yoshinori_hisakawa/items/a944115eb77ed9247794)

・SOLIDの原則(https://qiita.com/yoshinori_hisakawa/items/25576a62123607a696f6)

・インタフェース(https://qiita.com/yoshinori_hisakawa/items/cc094bef1caa011cb739)


クリーンアーキテクチャを採用することで嬉しいこと

それではここからはアーキテクチャについて説明していく。

クリーンアーキテクチャ、オニオンアーキテクチャなど

どのアーキテクチャでも採用することで得られることは共通していると思う。

それでは、具体的に説明して行く。


フレームワークに依存しない

ソースコードは進化していくものであり、常に変化していくものである。

新しく便利なフレームワークに変更するときも、ドメインレイヤーに影響を与えることなく簡単に実現できる。


・テストが可能で書きやすい

各レイヤー同士が疎結合になっているため、テストが描きやすく。

少しの改修で、多くのテストが壊れることもない。


・UIから独立

UIは変わるものであり、UIが独立している事で、簡単に修正や変更が行える。

UIの変更がドメインに影響を与えることがなくなる。


・データベースから独立

どんなDBを使っても良い。

最初はpostgreSQLを使っていたがmysqlやMongo,Redshifなど変更することが出来る。

しかもドメインレイヤーなどに影響は受けない。


・外部機能独立

ドメインレイヤーは外部の事をなにも知らない。

なので外部機能を独立させることが出来る。


どうやって嬉しいことを実現するか?

ここからは「採用することで嬉しいこと」これをどう実現していくか説明していく。

ここからは、常に「依存関係がどうなっているのか」を意識して読む必要が出てくる。

以下の切り口で実現の方法を説明して行く。

アーキテクチャの図を見て各レイヤーの定義を理解していく。← イメージを掴む

・パッケージ構成と実際のソースコードを見て理解していく。← イメージを実装に落とし込む


クリーンアーキテクチャの図を用いて理解 ~イメージを掴む~

CleanArchitecture-pic.jpg

これがクリーンアーキテクチャの有名な同心円の図である。

それでは各レイヤーの役割を見て行く。


JavaScript おじさんが教える本当の Modern JavaScript 入門 1

<a class="keyword" href="http://d.hatena.ne.jp/keyword/JavaScript">JavaScript</a> おじさんが教える本当の Modern <a class="keyword" href="http://d.hatena.ne.jp/keyword/JavaScript">JavaScript</a> 入門 1


JavaScript おじさんが教える本当の Modern JavaScript 入門 1



本発表における Modern JavaScript とは何か

Phase 1 として以下の環境での開発ができれば Modern JavaScript に入門できたものとする。

  1. Webpack, Parcel 等の module bunder による "module system" の活用。
  2. ES2015+ や TypeScript といった "Alt JS" の使用。それに伴う Babel によるトランスコンパイルツールの使用。
  3. ESlint, Prettier といった集団開発における、ソース品質安定のためのツール の使用。

ここまでのツールは、生産性が大きく上がるので、全フロントエンド開発者が使用すべきだと考える。




そもそもなぜ伝統的な JavaScript で書かないのか?

入門書で解説されている書き方と、実際の開発で使われている書き方は現在大きく異なる。なぜなのか?

// 入門書で学習する関数の宣言
function func1(val) {
console.log(val);
}

func1("func1 test");

// 実際の開発で散見されるアロー関数(ラムダ式)による宣言
const func2 = val => console.log(val);

func2("func2 test");

JavaScript 特有の、扱いが難しい挙動をなるべく取り除き、一般的なプログラミング言語は持っているが "JavaScript にはない" 諸機能を与えるため。

今回の開発ツールを使用しない生の JavaScript で書くことはほぼないと思っていただいていい。むしろ生の JavaScript の方が「圧倒的に難しい」(体感的には三倍難しい)




1. Webpack, Parcel 等の module bunder による "module system" の活用



他のプログラミング言語には当然あるが、"JS にはない" module system を使用するためにバンドルツールを使用する。

// module ( = 別ファイルに切り出した JS ファイル) を読み込む
import module1 from './module1.js'
import module2 from './Module2.js'

module1();
const Object1 = new Module2()

これがないと、こうなる。依存関係を解決するために「特定の順番で」ファイルを読み込まなくてはいけない。ファイル数が膨大になると人間が管理することは不可能になり、アプリケーションは破壊される。

<html>
<head></head>
<body>
<script src="js1.js"></script>
<script src="js2.js"></script>
<script src="js3.js"></script>
<script src="js4.js"></script>
<script src="js5.js"></script>
</body>
</html>




Module Bundler の比較

有名なものは以下。

  • Webpack: 大規模開発案件ではほぼこれ。
  • Browserify: Webpack が主流になる前に主流だった。
  • Parcel: バンドルツールの複雑な設定を廃し、簡易的に使える。
  • Rollup: ライブラリ開発者がよく使っている。サイズが小さいらしい。

最終的には Webpack を使うようにする。しかし、まずは Parcel でモダン開発環境を導入する。




2. Alt JS の使用と Babel によるトランスコンパイル




Alt JS とは

(正確ではないかもしれないが) Alt JS とは ブラウザでは動かない、もしくは動かない可能性のある Syntax で書かれた言語で、最終的には JavaScript にトランスコンパイルされるもの。

  • ES2015+: JS のスタンダードな仕様だがブラウザがまだ追いついていない、おいおい採用される、検討中だが使いたいので追加された、といった syntax の総称。ほぼ JavaScript の見た目。
  • TypeScript: 静的型を JavaScript に組み込むための言語。ほぼ見た目は C? ですか? わかりません。
  • Elm, ReasonML とかもあります。がユーザー数からいうと上記二つがほとんど。 




そもそもなぜコンパイルする必要があるのか

フロントエンドの主戦場である「ブラウザ」が実行できるのは JavaScript のみ(WebAssembly 等は一旦脇に退ける)

そのため最終的には JavaScript にするしかない。

かといって生の JavaScript にはない機能がありすぎる。

そのため、何らかの言語で書いて、最終的に JavaScriptコンパイルするしかない。




Babel でトランスコンパイルする

試しに CLI ツールでコンパイルする

npm install --save-dev babel-cli # CLI をいれる

npx babel src -d lib 
# src の中身をガッとコンパイルして lib に吐き出す

Babel の設定は .babelrc というファイルに書く(ディレクトリ直下に置くのが簡易的な開発では一般的)

例えば Babel の挙動を変更するプラグインをインストールして、それを使う設定

npm install babel-preset-env --save-dev


.babelrc

{
"presets": ["env"]
}

Parcel を使っておけば Babel によるトランスコンパイル, Module Bundle はやってくれるのでまずは後半の演習でこれを使う。




ESlint, Prettier といった集団開発における、ソース品質安定のためのツールの使用




定番ツール

  • ESLint: 不備があったり、バグに繋がりやすい syntax で書かれているものを見つけてエラーを吐くツール。そのルールもカスタマイズできる。
  • Prettier: 主に JavaScript のコードを成形してくれるツール。誰が書いても、このツールを実行すればかならず同じ形にしてくれるので、楽。




Prettier を使う

npm install --save-dev --save-exact prettier # インストール

WebStorm を使っている人なら command + option + shift + p で勝手に Prettier が発動する。Atom, VSCode 等々各種エディターにプラグインがあるのでこれを使うのが良い。

設定ファイルに細々書く。

  • シングルクオートを使う
  • セミコロンは使わない
  • 追コンマは常に使用する


.prettierrc

{
"singleQuote": true,
"semi": false,
"trailingComma": "all"
}

するとこうなる。

const obj1 = {
name: 'name1',
age: '30', // 追いコンマ
}




演習1: Parcel で Modern JavaScript 開発環境を作る

https://github.com/superyusuke/minumum-modern-front-end-setting

  • parcel-bundler が本体
  • node-sass は sass を使う際に必要
  • babel-plugin... は babel に色々なシンタックスを使用できるようにするために必要


package.json

{
"dependencies": {
"react": "^16.4.1",
"react-dom": "^16.4.1"
},
"devDependencies": {
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"node-sass": "^4.9.3",
"parcel-bundler": "^1.9.7",
"prettier": "^1.14.2"
}
}

npm script で parcel を実行する。


同じくpackage.json

"scripts": {
"start": "npm run parcel:dev",
"parcel:dev": "npx parcel src/index.html",
"parcel:build": "npx parcel build src/index.html --out-dir build"
}

src/index.html で src/js/index.js を読み込んでいる。この js ファイル内で import されているファイルは全部うまいことバンドルしてくれる。Babel でトランスコンパイルもしてくれる。

Screen Shot 2018-08-22 at 5.18.15 PM.png



すると次のようにモダンな JS を書く環境ができる。

import '../scss/index.scss' // sass を読み込む
import 'babel-polyfill' // async 系で必要っぽい

// import module
import module1 from './module1'
console.log(module1, 'module 1')

import React from 'react'
import ReactDOM from 'react-dom'

// react component
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div> }
}

// render react component
const mountNode = document.getElementById('app')
ReactDOM.render(<HelloMessage name="Jane" />, mountNode)

// async / await
const url = 'http://api.myjson.com/bins/159wqn'
const asyncFunc = async url => {
console.log(await fetch(url), 'fetch result')
}

asyncFunc(url).then(() => console.log('fetch done (in then)'))

// object rest spread
const obj1 = { value: 1 }
const obj2 = { value: 2 }
const obj3 = { test: 'hey' }
const combinedObject = { ...obj1, ...obj2, ...obj3 }
console.log(combinedObject, 'combined object')

const func1 = ({ test }) => console.log(test)
func1(combinedObject)




Phase 1 のまとめ

  • Parcel で module system が使えるようになった。
  • Parcel で Babel の処理をしてくれるので、ES2015+ syntax で JS を書くことができるようなった。
  • Parcel が勝手に JSX も処理してくれるので React コンポーネントを JSX で書けるようになった。
  • Prettier でコードが成形できるようになった。




Phase 2 の展望

モダンなパラダイムJavaScript を書く

  • var, let を書かないで、ほぼ全部 const にする。
  • そのためには functional programming に則っていく必要あり。
  • map, reduce 等をうまく使う。
  • immutable




Phase 3 の展望

非同期処理をモダンに書く

  • Callback の根絶
  • Promise の理解
  • async / await を使う
  • redux/vuex 関係でも async/await を使う

const url = 'http://api.myjson.com/bins/159wqn'

const asyncFetch = async url => {
const res = await fetch(url)
const { data } = res
console.log(data)
}

asyncFetch(url).then(() => console.log('done'))




Phase 4 の展望

Virtual DOM を用いた View ライブラリの活用

  • ReactJS
  • VueJS
  • Redux/Vuex