s平面の左側

左側なので安定してます(制御工学の話は出てきません)

Vuetify.js でお手軽マテリアルデザイン + Atomic Design「風」のディレクトリ構成

Vue.js #4 Advent Calendar 2017 - Qiita の 19 日目のエントリー。

(先日の記事の続きを書こうと思っていたが、こちらの日が先に来てしまった)


最近、フロントエンドエンジニア・デザイナー不在のプライベートな開発において、フロントサイドの実装を任されることがあった。

その際に Vuetify.js を用いて比較的簡単にマテリアルデザインのページを実現できたので、その備忘録。

(意見・ツッコミなどは歓迎)

vuetifyjs.com

出来上がった画面のイメージはこんな感じ。

f:id:okashoi:20171218114611p:plain

※グラフ描画は Vuetify.js で提供していないので、 vue-chartjs を利用している。

このレベルであれば、Vue.js の基礎知識だけある状態から1日で辿り着くことが出来た。

概要

Vuetify.js はマテリアルデザインのコンポーネントを80種類以上提供している。

Bootstrap などでグリッドシステムに慣れていれば、部品を組み合わせる感覚でいい感じにページを作ることができる。

f:id:okashoi:20171218115307p:plain

導入方法として CDN からの読み込み、 npm でのインストールどちらにも対応している。

また、8種類の vue-cli テンプレートが提供されているので、新規プロジェクト作成時にはこちらを利用してもいい。

基本的な使い方の解説や、利用できるコンポーネントについては 公式ドキュメント や、世の中にある他の記事にお任せ。

ディレクトリ構成

前提

  • ビルドには Webpack を用いる
  • 1ページに対して、1 つの html が 1 つの JS ファイルを読み込む形(SPAではない)

また今回の試みとして、いい感じにディレクトリを切って .vue ファイルからなるコンポーネント郡を分類したかったので、その基準として Atomic Design*1 を参考にしてみた。

ただし、私が Atomic Design についてきちんと理解・正しい解釈ができていなかったため、本来の Atomic Design 考え方からは外れてしまっている点に留意。 *2

+ .babelrc
+ webpack.config.js
+ package.json
+ src/
  + entries/        # Webpack のエントリーポイント
  | + landing.js
  | + main.js
  | :
  + Components/     # コンポーネント郡
  | + Templates/
  | | + Footer.vue
  | | + Header.vue
  | | - Contents/
  | |   + Landing.vue
  | |   + Main.vue
  | |   :
  | + Pages/
  | | + Landing.vue
  | | + Main.vue
  | | :
  | - Organisms/
  |   + AForm.vue
  |   + ATable.vue
  |   + AChart.vue
  |   :
  - common.js       # 共通の処理を記述したモジュール

コンポーネント.vue ファイル)名は仮

ディレクトリの説明

以下ソースコードでは、 src/App というalias を設定している。

また前述の通り おそらく、本来の Atomic Design 考え方からは外れてしまっている という点に留意。

entries

Webpack のエントリーポイントが配置される。

エントリーポイントでは Vuetify.js の読み込みと、Pages コンポーネントの render を行っている。

例) src/entries/main.js

import Vue from 'vue';
import Page from 'App/Components/Pages/Main.vue';
import Vuetify from 'vuetify';
import 'vuetify/dist/vuetify.css';

Vue.use(Vuetify);

new Vue({
    el: '#app',
    render: h => h(Page)
});

Pages

「このページはヘッダがあって、フッタがあって、このコンテンツがある」といった具合にページ全体の構成を表す。

ヘッダやフッタ、コンテンツにあたる部分が Templates コンポーネントとなり、これを配置する。

例)src/Components/Pages/Main.vue

<template>
  <v-app id="inspire" v-cloak>
    <my-header></my-header>
    <my-content></my-content>
    <my-footer></my-footer>
  </v-app>
</template>

<style>
  [v-cloak] {
    display: none;
  }
</style>

<script>
    import Header from 'App/Components/Templates/Header.vue';
    import Footer from 'App/Components/Templates/Footer.vue';
    import Content from 'App/Components/Templates/Contents/Main.vue';

    export default {
        components: {
            'my-header': Header,
            'my-content': Content,
            'my-footer': Footer
        }
        // ページ全体に及ぶロジックを記述
    };
</script>

Templates

共通のヘッダやフッダ、各ページのメインコンテンツにあたる部分など、ページを構成する各要素。

各ページのメインコンテンツにあたるコンポーネントは Contents/ というディレクトリを作成し、その下に置いた。

Organisms コンポーネントを、グリッドシステムに基づいて配置する。

例)src/Components/Templates/Contents/Main.vue

<template>
  <v-content>
    <v-container grid-list-lg>

      <v-layout row>
        <v-flex md12>
          <my-chart
            :chart-data="chartData"
          ></my-chart>
        </v-flex>
      </v-layout>

      <v-layout row>
        <v-flex md4>
          <my-table
            :table-items="tableItems"
          >
          </my-table>
        </v-flex>

        <v-flex md8>
          <v-layout row>

            <v-flex md6>
              <my-form
                v-model="formValue"
              >
              </my-form>
            </v-flex>

            <v-flex md6>
              <my-card
                :card-text="cardText"
              >
              </my-card>
            </v-flex>

          </v-layout>
        </v-flex>
      </v-layout>

    </v-container>
  </v-content>
</template>

<script>
    import MyCard from 'App/Components/Organisms/ACard.vue';
    import MyChart from 'App/Components/Organisms/AChart.vue';
    import MyForm from 'App/Components/Organisms/AForm.vue';
    import MyTable from 'App/Components/Organisms/ATable.vue';

    export default {
        components: {
            'my-chart': MyChart,
            'my-table': MyTable,
            'my-form': MyForm,
            'my-card': MyCard
        },
        data: () => ({
            chartData: [],
            tableItems: [],
            formValue: {},
            cardText: ''
        })
        // ロジックを記述
    };
</script>

Organisms

特定のフォームや表など、機能を持つ個別の部品。

ルート要素を <v-layout> にすることで Templates 内で利用する際に、グリッドシステムの恩恵を受けられる。

v-layout > v-flex の構造をネストさせてもレイアウトに影響しないよう)

Molecules を組み合わせて作る。

Vuetify.js が提供しているコンポーネントがその Molecules にあたる。

(下の例では v-data-table

例)src/Components/Organisms/ATable.vue

<template>
  <v-layout row>
    <v-flex md12>
      <h2 class="ml-3 mb-2">表1</h2>
      <v-data-table
        :headers="headers"
        :items="tableItems"
        hide-actions
        class="elevation-1"
      >
        <template slot="items" slot-scope="props">
          <td class="text-xs-center">{{ value1 }}</td>
          <td class="text-xs-center">{{ value2 }}</td>
          <td class="text-xs-center">{{ value3 }}</td>
        </template>
      </v-data-table>
    </v-flex>
  </v-layout>
</template>

<script>
    export default {
        props: {
            tableItems: {
                type: Array,
                required: true
            }
        },
        data: () => ({
            headers: [
                {
                    text: '要素1',
                    value: 'value1'
                },
                {
                    text: '要素2',
                    value: 'value2'
                },
                {
                    text: '要素3',
                    value: 'value3'
                }
            ]
        })
    }
</script>

Molecules / Atoms について

原則、 Vuetify.js が提供するコンポーネントがこれらに該当するので今回は自分では作成しなかった。

他のライブラリを用いるなどして部品を作る際には Molecules を作るかもしれない。

感想

今回は慣れないフロントエンド周りの設定・実装方針策定を1からやったので、いろいろ試行錯誤しながら進めた。

Webpack の設定含めて迷うことも多かったが、一旦納得行く形には落ち着いた(Atomic Design をきちんと取り込めていないという問題はあるが、現時点でこのプロジェクト内での破綻は起きていない)。

システムが肥大化するに従って新たな問題は出てくるとは思うが、そのときにきちんと対応できるようにフロントエンドの設計思想を学んでいきたい。

*1:http://atomicdesign.bradfrost.com/chapter-2/ を参照

*2:今回の例で言えば、例えば Pages と Templates の関係が逆なような気がしている。

Atomic Design にあわせたコンポーネント分割のしかたは次のリポジトリが参考になるとのこと(ただし React の例)。

github.com

Laravel 5.4 におけるユーザ権限管理の実装に関する考察(途中まで)

背景

業務において、同じチームのメンバーがプルリクエストを WIP で出し「こんな方法でいいか?」という旨の相談を投げかけてきた。

それについて議論になったことがあるので、それについて書いておく。

そのときの論点は大きく分けては 2 つあったのだが、今回はその 1 つについて書く。 もう 1 つは気が向いたときに書くかもしれない。

※以下はあくまで私個人の意見であり、「こうすべき」と言っているのではないことに留意。意見・ツッコミ等は歓迎。

実現したいこと

前提:Laravel 5.4 で開発している web システム

ユーザごとに「このデータは編集できる」「このデータは閲覧のみ可」といった権限管理をしたい。

登場するテーブルは以下の2つ。

(テーブル名は実際のものとは異なる。あくまでイメージ)

  • users
    • ユーザのテーブル。後述する groups テーブルの id を外部キーとして持つ。
  • groups
    • 権限グループのマスタテーブル。id と「閲覧のみ」「管理者」といった表示用の名前 name のみを格納。

プルリクの中身

いろいろ端折っており、あくまでイメージ。

実際にはもう少し細かい条件分岐などがある。

論点は「Model 内に Controller のアクションのリストが存在する」という点である。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Group extends Model
{
    protected $fillable = [
        'id',
        'name',
    ];

    /**
     * 権限リストを取得
     *
     * @return array
     */
    public function getPermissions()
    {
        //----------
        // 閲覧のみ
        //----------
        if ($this->id === 1) {
            $permissions = [
                'UserController@index'   => true,
                'UserController@update' => false,
                'UserController@delete'   => false,
            ];
        }

        // 以下略

        return $permissions;
    }

}

この Model を介して、各 Controller のアクションが実行できるのかを判定する。

私の意見

MVC 各層を次のような階層関係として捉える。

View
| ↓
|  Controller
↓ ↓
Model

※矢印は「呼び出し元→呼び出し先」という関係

このとき前述の「Model 内に Controller のアクションのリストが存在する」という状態はすなわち「呼び出される側が呼び出し元について知っている」ことになり Group モデル が「知りすぎている」状態になっている。

実装例

ユーザを弾くかどうかの処理は View → Controller までの間(Controller を含む)の責務であり、Laravel においては Middleware がそれにあたる。

権限グループごとにアクセスを弾く Middleware を作成し、routes にてアクションごとに必要な Middleware を設定してあげればよい。

<?php

namespace App\Http\Middleware;

class DenyReadonlyUser
{
    /**
     * 「閲覧のみ」ユーザがアクセスしたとき 403 エラーを返す
     */
    public function handle($request, \Closure $next)
    {
        if (\Auth::user()->group_id === 1) {
            abort(403);
        }

        return $next($request);
    }
}

(Http\Kernel.php の設定は省略)

routes/web.php

Route::get('/users', 'UserController@index');
Route::post('/users/{id}', 'UserController@update')->middleware('deny.readonly');
Route::delete('/users/{id}', 'UserController@delete')->middleware('deny.readonly');

※ただし、この方法ではブラックリスト方式になってしまうので安全ではないという懸念がある。

考察……はまた今度

本当はそれぞれのメリット・デメリットを比較しようと思ったのだが、このあと私用で出かけてしまいアドベントカレンダーの期限に間に合いそうになくなるので、いったんここまで。

残りの考察についてはまた別途書きたいと思う。

JAWS FESTA 中四国 2017 in 愛媛松山 に参加してきた

jawsohenro.doorkeeper.jp

これまで AWS はほとんど触ったことないし、愛媛松山に特に縁があるわけでもないのだが、友人に誘われたのをきっかけにノリと勢いで愛媛まで足を運んだ。

何気にこれで四国初上陸だったりする。

クラウドでつながり、今を、明日を変えていく」というテーマのもと、AWS に限定せず「クラウド×地方」が取り上げられていた。 参加者は全部で 170 名ほど。

聴講したセッションのメモ

基調講演:武闘派はコミュニティに生きる。

フジテックのCIO、友岡さんによる3つのお話。

メモ

  • 「コミュニティの活動等になぜ参加したら良いか?」を理論武装する
    • 弱いつながりの強さ
      • 交流頻度が少なく多様性があるのでブリッジが生まれやすい
        • コミュニティ、SNS
        • イデアを創出するのに必要
      • c.f. 強いつながり
        • 「タバコ部屋」の世界
        • 組織として実行するために必要
      • 遠いところと弱くつながるように心がける
        • 一方で会社内では強いつながりを作る
      • スモールワールド現象
      • ストラクチャルホール理論
        • 結節点(ブローカー)を介してしかやり取りできない状態
        • ブローカーは得をする
        • H型人材になろう = 2箇所(以上)に軸がある
      • 多様性は時間とともに失われる(同質化)
        • 同質化のプレッシャー3つ
          • 規範的圧力→「やってはいけないとはいってないけど、やったことないからダメ」
        • 常識は同質化が生み出した幻想
          • ビジネスとしての打ち手
            • 常識に従う
            • 常識をうまく活用する
            • 破壊して塗り替える
      • 同一性
        • 人間は似たものと交流する
        • 誰と付き合うかで自分が変わる
        • 人間はそもそも多様性を求めていない
        • →会社の中で同質化していることを意識する
  • ダメな情シスに対する処方箋
    • CIOがいないから
      • IT の話が役員会の場でできない
    • 資源依存理論
      • 3つの対抗策
    • 思考のフレームワークを使う
    • 取りうる戦略(思考のフレームワークに基づく)
      • 例1
      • 例2
        • 下位20%に合わせる→上位20%の人が欲しているものは過剰性能
        • 上位20%に合わせる→最新のモノが安い・早い
  • 若いエンジニアに贈る言葉
    • 好きなこと・できること・やらなければならないこと
      • 一致すると幸福
      • できることが小さく、やらなければならないことがずれていると不幸せ
      • 一致していないときどうするか
        • やらなければならないことに集中する
        • できることを増やす→誰よりもうまくできるように努力する
        • それを、好きになる
      • やりたいこと
        • それって人よりうまくできるの?
        • あなたがやらなければならない理由はあるの?
    • PRIDE と BRAND
      • PRIDE
        • 邪魔になるもの
        • 「どれだけ自分が気持いいか」
      • BRAND
        • 信用の積み重ねでつくられるもの、期待値
        • 他人によって作られる
        • 頼まれていないことをやっておく
          • 「お役に立てる機会が得られた」
        • イチローは会ったこともない、何も約束していない人から「ヒットを打つ」ことを期待されている
    • 「働く」4つの発展階層
      • Art → Play → Work → Labor
        • Wrok
        • Play
          • プロフェッショナル
        • Art
          • マイスター
          • 必要なのは時間ではない

所感

「周りの人を誘ったけど来てくれなくてぼっちの人」を勇気づける発表。ぼっち最強。

考え方の切り口として、弱いつながり・強いつながりを活用していきたい。

(香川) 陸と離島の距離を「ゼロ」に!次世代無人物流システム「KAZAMIDORI」実現に向けた取り組み

株式会社かもめや 小野さん

メモ

  • 離島大国日本
    • 6000以上うち 412 が有人
    • 18:00代に最終フェリー
      • 時間外は海上タクシー・漁船・自家用船などで移動
      • 緊急時は防災ヘリ・ドクターヘリ・救急艇
    • 離島・僻地過疎地人口 1000万人
      • 買い物難民 700万人
      • 医薬品のうけとりも大変
      • 人手不足・高コスト
      • 定期航路が現象 2008年100往復→現在5往復
    • 離島では、日本で未来に起こるであろうことが先んじて起こっている
  • ドローンを使った無人物流
    • 課題
      • 日本特有の面積の狭さ
        • 離着陸用地の確保
        • 新航空法への対応
    • ハイブリッド型(島国モデル)
      • 陸海空の組み合わせ
    • やっていること
      • 全航路の輸送コスト計測
      • 輸送ルート最適化・定期航路の設定
      • ドローンポートの場所確保
      • 山間
        • 海よりも難しい→火災など
      • AWS IoT
        • Lambda
        • DynamoDB
        • Cognito
    • プロジェクトサポーター→「かもめーず」で検索

所感

普段関わることの少ない IoT のお話。 これを聞いて「本当に困っている人を助ける」のは IoT のような領域なのかなあ、と感じた。

多くのインターネット上のサービスというのは比較的「恵まれている」人をより豊かにするものだと思う。

どちらが優れている、劣っている、という話ではなく。性質の話として。

(広島) OpsWorksを使ったミニマムDevOps

Wardish合同会社 三戸さん

メモ

  • 社員4人で開発だけでなく運用も回す
  • DevOps
    • できるだけ属人作業を減らす
  • devops のメリットを妨げる(Portability を妨げる)要素
  • 対策
    • 1プロジェクト1環境
    • キッティングツール
    • 環境依存のパラメータ化
  • キッティングツール
    • beanstalk に載せるのが一番楽
      • ライバルがたくさんでてくる
      • フルマネージドだとロックインが怖い
      • 教育が入っているのも大切(フルマネージドだと教えたいことが教えられない)
    • Cloudformation → アプリエンジニアが使うには大変
    • Chef + KitchenCI + BERKSHELF を採用している
      • BERKSHELF 使うために Chef は Ver12 以降を使う
      • KitchenCI → ネット上に古い解説が多い→公開日で絞り込んで検索しよう!
    • BERKSHELF
      • コミュニティレシピは使わない方がいい
    • Docker?
      • ノウハウ特殊で属人化しがち(Docker 特有のノウハウ)
        • インフラ・サーバの勉強にならない
  • 環境展開は Vagrantfile のみ
    • Vagrant の add-in 等はホストでそろえる
  • OpsWorks はリポジトリを指定ディレクトリにデプロイしてくれる
    • Vagrant の sync-folder と相性が良い
    • デプロイ次に色々処理したいときはレシピに書いておく
  • OpsWork
    • CodeCommit に限定しない
    • パラメータはデプロイスクリプトで書き換える方針 → 初心者のミス防止
      • 書き換えるファイルはプロジェクトで方針を決める
    • CloudFormation との役割分担
    • Time-based
      • 製造業→深夜早朝・土日を止めることで、お安く
      • 日曜の早朝だけ動いてバッチを実行する簡易バッチサーバとして
  • どちらかと言うと Ops が Dev に食い込むイメージ
    • 逆はいつまでたっても Dev が終わらないパターン

所感

環境構築の問題とか「あるある」だなあ、と思いながら AWS 等を活用した一つの事例を聞けたので、非常に興味深かった。

(島根) CMS開発者 + データセンターエンジニア = AWSでdevOpsに取り組み始めた

株式会社 ティーエム21 吉岡 さん

メモ

  • 社外(自前のデータセンター)のエンジニアを巻き込んで、運用の質を上げつつ自分たちが開発に集中できる環境をいかにして手に入れたか
  • 自社CMS
    • 行政向け
    • 実際のサイト作成者が作ったCMS
    • アプリケーションを管理と公開で分離
      • DBへのアクセス権限を制御
      • アプリケーションとしてカスタマイズするのは公開側→管理がしやすい
  • 問題点
    • 障害時にApacheのチューニングで対応(アクセス制限など)
    • 共用サーバのため、融通がきかないことが多々ある
  • まずは一部サイトAWS
    • 自分で運用するようになって管理コストが降ってきた→相手を巻き込むように
  • AWS の良さをアピール
    • 「せっかくある技術を使わないのもったいないですよ」
  • CloudFront
    • キャッシュがすごく便利
      • 当初は WAF 目的で導入
      • 管理画面・公開画面で切り分けているので複雑な設定いらない

所感

データセンターを持っている会社の中の人に AWS を勧め、導入していく話。

結果双方(サービスの質も上がって三方)幸せになっているので、このように「ものごとを進める力」というのも大切だなあ、と。

Node.jsとServerless FrameworkではじめるAlexa Skills作成入門

株式会社デジタルキューブ オカモトさん

メモ

  • Amazon Alexa
  • amazon echo
    • Skill = Alexa で使えるアプリ
    • Alexa / Amazon Echo 年内に国内
    • 受話→音声認識→WebAPI→音声変換→発話
    • 音声認識・音声変換は Alexa Skills Kit で完結
      • 言語判定もやってくれる
    • WebAPI を AWS Lambda 等で実装すればよい
    • JSON を受け取って JSON を返す
  • Intent, Slot 引数のような形で渡ってくる
  • sample が優秀

所感

中身の技術としても、アプリケーションとしても元言語処理の人として興味深い。

(岡山) 週末趣味のAWS ElasticBeanstalk編 その2(仮)

株式会社リゾーム 難波 さん

メモ

  • Elastic Beanstalk
    • クラウド環境作成・構成管理ツール
    • 開発者ガイド(日本語・pdf)
      • ボリューム大
      • だいたいこれを読めば解決する
      • サンプルも豊富
  • Application > Environment
    • Environment を組み合わせて Application を構築
    • Application 内で異なるバージョンの Environment 切り替えられる
    • Application 内で異なる機能の Environment
  • multi-container docker
    • EC2
    • Dockerrun.aws.json
    • .ebextensions
      • ファイルの番号順に実行される
  • CodeCommit などで構成を管理できる
  • multi-container docker を使うときの注意
    • オートスケーリング グループ ヘルスチェックタイプ
      • EC2 ← デフォルト
      • ELB ← こっちのほうが良いのでは?説
    • docker プロセスが停止したとき、再起動しない
    • docker プロセスが再起動したとき ecs-agentコンテナ再起動しない
    • ELB ならよしなに再起動してくれる

所感

開発者ガイド読もうと思った(小並感)。 私自身が AWS の各種サービスに明るくないので詳細まで追い切れなかったが、それでも丁寧な検証とデモを合わせて見やすい・分かりやすい発表だった。

パネルデスカッション クラウドとコミュニティを活用したこれからの働き方

  • コガさん DIGITALCUBE
  • クラヌキさん ソニックガーデン
  • 大石さん サーバーワークス
  • 水戸さん サイボウズ

メモ

  • サイボウズ「働き方改革に関するお詫び」
  • 個々人に合わせた働き方 = 多様性だ
    • 一律で与えられるものではない
    • 一律に決めたほうが良いかはお仕事の内容による
      • エンジニアの仕事は多様なので決められない
  • 「同質化」すると変化に弱くなる
    • 社長が変化を掲げてもアクセルがかからない
    • 優秀な人材を入れるために会社の制度を変えていった
      • 外の物差しを入れていった
  • エンジニア採用は東京だと難しい→全国から
    • 必然があってそうなった
  • リモートワーク・副(複)業は信頼がなければ成り立たない
    • 一定のスキルセット・人事制度
  • 文字ベースのやり取りのガイドライン
    • 否定しない
    • 叱責しない
    • 2回やって伝わらないなら face to face
  • リモートワーク・副(複)業は人によって向き不向きがある
    • 「仕事をすれば良いんでしょ?」では作業者と同じになってしまう
      • 中の人になる、それができないと情報がインプットされない
  • 会社は最後のコミュニティになっている
    • 同質化を回避する
    • 世界を拡張する

所感

もし、働き方改革が「働き方の多様性を実現すること」なのであれば、 これは会社側だけでなく、働く人自身も「自分の働き方を他人に強要しない」という変化が必要なのだろう。

また、東京でエンジニアを採用するのは難しくなってきており、優秀な人材を採用するためにリモートワーク・副(複)業という仕組みを取り入れなければならない、という考え方はなるほどな、と思った。

全体所感

今回は内容も開催地域も普段とは全く異なる場所だったので、はじめは「せっかく愛媛まで足を運んだのに、何もわからずに終わるんじゃないだろうか?」という不安もあった。

しかし基調講演にて「ブリッジ」「弱いつながり」「ぼっち最強」の話を聞いて、むしろこれまで全く関わってこなかった領域で「弱いつながり」を作る絶好の機会であると考えた。

今回は

  • AWS
  • IoT
  • 愛媛松山

など、新しいことを知る・触れるきっかけがたくさんあり、「多様性」がキーとなった一日だった。

場所が変われば、考え方も変わるものだなあ、と改めて感じた。

AWS は楽しそうなので、 Lambda あたりから触っていこうと思う。

最後に

運営委員のみなさま、登壇者のみなさま、本当にありがとうございました!