WEB+DB PRESS Vol.112
読んだ感想とまとめ
縁の下のUIデザイン カードUIの向き不向き
- スマホUIは現実のものを模したデザイン(スキューモフィズム)からフラットに
- カードUIはなんとなくとっつきやすく使いがち
- カードUIは立体感や影をつけて使うことから、相対的に要素に存在感を出すことができる
- 強弱をつけたいデザインに効果的
- 不均一な情報をきれいに整理できる
- 例えば高さが違ったり
- デザインに必要な要素が多いのでごちゃつかないように意識する
- カードによる入れ子は余白が多くなるのでやめたほうが良い
- ツイッターのような短文の投稿の場合1つ1つの重みより全体のみやすさが重要なので、カードUIは適さない
- レビューのようなしっかりしたものはカードUIで
- PCではテーブルが多いがそれをカードUIにするときは気をつける
- スマホでテーブルを使うと横にスクロールする必要があり使いづらい
- 逆にPCでカードを使うとテーブルと比べて見づらい
- カードUIと違うUIを作り比べることで使う理由をきちんと明確化する
at the front 今生きるプログラマーが、この仕事をあこがれのものにする
- 「高度なコンピュータ・サイエンス、プログラミングスキルの現場適用の難しさ」が課題だった
- フレームワークの中にある低層の基礎の必要性
- 木構造の差分適用というアルゴリズムがUIの更新を抽象し、パフォーマンスと開発効率を両立させる
- 高度な技術もそれを必要とする人に説明しなければ、技術で現実的に信頼できる品質で実装できなければ、有用であると言えない
- 適切な場所とタイミングで提示するためには、求められているさまざまな期待を理解し超える必要がある
- プログラマーが憧れの対象であるために社会の期待に答え、本質を追い求める必要がある
- FWが死んでも思想は継承されるはず
- 流行り廃りは必ず発生して、変化を受け入れる覚悟必要である
Special Report ScalaMatsuri 2019
- 2019/6/27 - 29
- 初日はワークショップ形式、2日目は公園形式、最終はアンカファレンス
Special Report elm Europe 2019
- 2019/6/27 - 28
- Elmによるゲーム開発、JSのパフォーマンスチューニング、スケール
モダンフロントエンドの構造化と分割の新提案 コンポーネント設計
- メンタルモデルを活用した設計、実装
- 関心の分離により疎結合を実現
なぜコンポーネント設計か
- アプリ開発は下記のようなフローで動く
- ニーズ分析/企画
- 要求分析/要件定義
- 仕様策定
- UIデザイン/プロトタイピング
- 設計/実装
- プロトタイピングツールの充実のおかげで、共有やFBしやすくなっている
- しかしデザインツールだけでは齟齬が発生することが度々ある
- デザインが完成してから開発をする場合、互いに関与できることは少ない
- 画面単位で考えているので待ちが長くなる
- 小さくすることで解決を図る(コンポーネント単位で考える
ビューコンポーネントの現在
- ビューコンポーネントは外部からデータを受け取り画面に表示をおこなう
- また、コンポーネント内のみ扱えるデータをもつ(ボタン非活性フラグ
- 複数コンポーネントを子コンポーネントでまとめられる
- 基本的に子コンポーネント同士でデータは受け渡しをせず親子で行う
- Reactではデータフローは一方向
import * as React from "react" import { Switcher, OptionLabel } from "./components" interface Props { heading ? : string; } interface States { display: boolean; } // クラスコンポーネント export class DisplaySwitchClass extends React.Component < Props, States > { constructor(props: Props) { super(props) this.state = { display: false } } render() { const toggleEvent = this.onChanged.bind(this); const display = this.state.display; return ( < > <StyledHeading> {this.props.heading} </StyledHeading> < OptionLabel text = "表示" onClicked = { toggleEvent } /> < Switcher toggle = { display } onChanged = { toggleEvent } /> < / > ) } onChanged() { this.setState({ display: !this.state.display }) } } // 関数コンポーネント + React Hooks import { useState } from "react" export const DisplaySwitchFunc = (props: Props) => { const [states, onChangeDisplay] = useState < States > ({ display: false }); const { display } = states; const toggleEvent = () => { onChangeDisplay({ display: !display }) } return ( < > <StyledHeading> {props.heading} </StyledHeading> < OptionLabel text = "表示" onClicked = { toggleEvent } /> < Switcher toggle = { display } onChanged = { toggleEvent } /> < / > ) } const StyledHeading = styled.h1 ` font-size: 16px; `;
- Vue.jsはデータフローが双方向、DOMに対してディレクティブという値を代入したり、イベントを設定できる
- 子から親へデータを渡せたりする
- stateは存在せず、dataオブジェクトがある
- 子から親へデータを渡せたりする
<template> <div> <StyledHeading> {{heading}} </StyledHeading> <option-label text="表示" :onClicked="onChanged" /> <switcher :toggle="display" :onChanged="onChanged" /> </div> </template> <script> import styled from "vue-styled-components"; import { OptionLabel, Switcher } from "./components/"; const StyledHeading = styled.h1 ` font-size: 16px; `; export default { name: 'display-switch', data: function() { return { display: false } }, props: { heading: String }, components: { OptionLabel, Switcher, StyledHeading }, methods: { onChanged() { this.display = !this.display } } } </script>
- ビューコンポーネントにおける再利用性
- どこまで分割を行うべきか
- デザイナーは下記2点を重要と考える
- ビジュアル(=見た目)や操作性に一貫性を持たせ、ブランドを守る
- デザイナーどうしの協業を推し進め、効率化を図る
- Atomic Designという考え方がある
- 粒度ベースで分割を行うと下記メリットが生まれる
- 開発メンバー間で意思疎通しやすくなる
- ビューコンポーネントとしてコードを小さくできるため、変更に強くなる
- 誰でも部品を組み合わせるだけで画面をある程度作成できる
- デメリット
- 最近は粒度で分割しないほうがいいのではと考えられ始めた
設計の前に考えること/行うこと
- アプリケーションの中でビューコンポーネントはどのような性質や役割を持つべきか考える
- アプリケーションがどういうものなのかを理解しておく
- 人間が理解するという活動は「学習」と「イメージ化」という大きな流れに分解できる
- この理解の流れで作られるイメージを「メンタルモデル」と呼ぶ
- 人や環境によってメンタルモデルがずれることがある
- トンカチを「釘を叩くもの」「ガラスを割るもの」とずれる
- 叩くは同じだが、何を叩くがずれている
- アプリケーションも同じようにずれることがよくある
- 文脈によっても同じメンタルモデルの話をしていてもずれる
- トンカチの柄だけを見ると、木の棒と認識してしまう
- サービスや業務の領域に関係するメンタルモデルからアプリケーションを理 解する
- 「サービスや業務の領域」のことを「ドメイン」と呼ぶ
- これは開発者だけでなく、使うユーザーにも言える
- メンタルモデルの抽出はユーザーの活動に注目し、「名詞と動詞の簡単な文章」にする
- 宛先管理サービスを例にすると
- ユーザーは、宛先をサービスに登録する
- ユーザーは、宛先の名前を記入する
- ユーザーは、宛先住所の郵便番号を記入する
- ユーザーは、宛先住所の都道府県、市区町村、丁目と番地を記入する
- ユーザーは、サービスに登録している宛先を変更する
- ユーザーは、サービスに登録している宛先を削除する
- ユーザーは、サービスに登録している宛先を参照する
- 名刺を抽出する
- 宛先、名前、郵便番号、住所、都道府県、、、
- その後名刺の定義を行う
- 例 宛先:宛先とは何かを届けるための情報。届ける先の人の名前と郵便番号と住所が必要
- 類義語はどちらの名詞を主とするか定義しておく
ビューコンポーネントの設計と実装
- ドメインのコンポーネントを作っていく
- 抽出した名詞をもとにドメインコンポーネントを作る
- グループ化できるものを探す
- 住所は都道府県、市区町村などを持つ
- 宛先は名前、郵便番号、住所などを持つ
- そのままコンポーネントに定義する
const Destination = (props: Props) => { const { name, postalCode, address } = props const postalCodeStrings = ` ${postalCode.upper} - ${postalCode.lower}` const addressStrings = ` ${address.prefecture} ${address.city}${address.street}` return ( <> <span>名前</span> <strong> { name } </strong> <span > 郵便番号 < /span> <strong > { postalCodeStrings } < /strong> <span > 住所 < /span> <strong > { addressStrings } < /strong> </> ) }
const Name = (props: Props) => { const name = props.value return ( <> <span>名前</span> <strong > { name } < /strong> </> ) }
<Name value={name} /> <PostalCode upper={postalCode.upper} lower={postalCode.lower} /> <Address city={address.city} street={address.street} />
- これらのコンポーネントはGUIを通して表示される
- ユーザーはwebのなどのGUIの部品にイメージを持っている
- また、画面というドメインモデルが存在する
- ドメイン/GUI/画面などのメンタルモデルを1つのコンポーネントの中で考えるべきではない
- 関心事によって分離する
- オフィスの受付にある受付アプリは「受け付け担当者を呼ぶこと」と「ボタンをタップしたら処理を開始すること」という2つの役割が存在する
- ドメインの関心は「受付担当者を呼ぶこと」
- GUIの関心は「ボタンはタップできること」
- 関心事とは、対象のモノ/コトに対して持つ役割や興味の範囲を指し
- 同じ関心事にあたるメンタルモデルやその振る舞いを1つにまとめて整理することが関心の分離
- ドメインオブジェクトは要素を持つ関心事
- GUIは表示の関心事を持つ
- アフォーダンスという環境から得られる情報を鍵にして最適化を行う
- 画面は遷移、何を表示するかに関心を持つ
- レイアウトというドメイン同士の間隔に関心を持つ
- 状態やコンテナ
- ドメインオブジェクトは、ドメインエレメントを要素として持つ
- ドメインエレメントは、GUIパーツで表示/入力を表現する
- GUI パーツは、組み合わせて使うことがある
- 画面は、面として表示し遷移をする
- レイアウトは、配置に関して調整する
- コンテナは、アプリケーションの状態データの流し込みを行う
- 画面での入力はコンテナを経由しドメインオブジェクトに流れ、ドメインエレメントを変更し、GUIが表示を反映する
開発フロー
メリット
- 並列的に、ビューコンポーネントの設計/デザイン/実装を小さく行えるようになる
- デザイナー/エンジニア間でアプリケーションを一緒に理解できるようになる
- デザイナー/エンジニア間で待ち時間などを少なくできるようになる
画面定義
- ユーザーが使いやすいアプリケーションはタスクベース(動詞的な画面のグルーピング)ではない
- オブジェクトベース(名詞的な画面のグルーピング)によるナビゲーション
これをOOUI(オブジェクト指向UI)と呼ぶ
ブログをタスクベースにすると
- 編集する
- 記事
- プロフィール
- テンプレート
- 追加する
- 記事
- テンプレート
となる
オブジェクトベースの画面遷移は
- 記事(一覧)
- 詳細
- 編集
- 追加
- プロフィール
- 変更
- テンプレート(一覧)
- 詳細
- 編集
- 追加
となり、わかりやすい
実践チュートリアル
- アパレルECサービスを例に設計をする
要求、要件定義
- 商品を閲覧して購入できる
- 商品はカテゴリ別やブランド別に分類して閲覧できる
- 購入したい商品をカートに入れてまとめて購入できる
- 購入したい商品のサイズやカラーなどを選ぶことができる
- 購入すると商品を配送会社を使って配送できる
- 配送にはいくつかの宛先を登録でき、その中から選ぶことができる
- 支払いには現金/クレジットカードとペイサービスを利用できる
画面遷移図やワイヤーフレームを書くのではなく、ユーザーやサービス提供者の活動を、名詞と 動詞の簡単な文章に表す
- ユーザーは、商品をカテゴリから探す
- ユーザーは、商品の一覧を参照する
- ユーザーは、商品の一覧から商品の写真やスペックなど詳細情報を参照する
- ユーザーは、商品詳細でバリエーション(サイズやカラー)を選ぶ
- ユーザーは、商品をカートに入れる
- ユーザーは、カートに入れた商品を確認する
- ユーザーは、購入を確定する
- ユーザーは、お届け先を記入する
- ユーザーは、お届け先を追加する
- ユーザーは、カートの商品に関して支払いを行う
- ユーザーは、決済完了したか確認をする
- ユーザーは、発送/配送状況を確認する
世に存在しないアプリの場合は、似たサービスをもとに考える
その後名詞の定義を行い、オブジェクトの抽出を行う
- 商品
- 商品名
- 写真
- カテゴリ
メンタルモデルが数多くあり、複雑なときは、メンバーによるワークショップ形式ですり合わせを行う
抽出後はOOUIで画面の定義を行う
タスクベースにすると探すから商品、カテゴリ、ブランドと分岐する
ユーザが何をしたいかというと
- A:決まっている商品を探す
- B:商品カテゴリを見る
- C:ブランドを見る
となるので、探すは興味のある商品に紐づく
商品からカテゴリやブランドに紐づく
検索から商品、カテゴリに紐づく
メンタルモデルから得られるもの
共通言語
建築ではパターン・ランゲージと呼ばれる
DDDではユビキタス言語
正しさの基準
- メンタルモデルを理解することで、何がアプリケ ーションにとって正しいか判断しやすくなる
- メンタルモデルは変わっていくものだが、基準を 一度作れば変化を捉えやすくなる
- ユーザーが持っているメンタルモデルと同じものを 持つため、ユーザーを理解することにもつながる
RDBMS徹底比較
- PostgreSQL、MySQL、SQL Server、Oracle Databaseを比較
- OS から見たアーキテクチャ、SQL、オブジェクトの 3 つの視点で比較
アーキテクチャを比べる
Oracle Database
- 大規模システムのRDBMSとして大きなシェアを持っている
- 高速なレスポンスが要求されるOLTP(OnLineTransactionProcessing)に対応できる
- 高スループットが要求される分析システムに対しても対応できる
- DB変更処理をオンライン状態のママ実行できる
- 障害対応のためのクラスタウェアやファイルシステムも提供する
- ライセンスなど高い
- RealApplicationClusters(RAC)はスケールアウトによる拡張で独自機能
- ストレージを共有する複数のホストで構成され、ホストの追加により仮想的に利用できるプロセッサ数とメモリ量を拡大する
PostgreSQL
- 比較的小規模なサイズに使われる
- 1970年代に開発されたIngresを継承し、1989年に開発されたPostgresを直接の子孫とする
- OSS
- Amazon Redshift、Amazon Aurora、 Greenplum Database、EDB Postgres Advanced Serverなどに派生する
- 拡張性が高く、自由度が高い
MySQL
- webのバックエンドなど比較的シンプルなSQLを高速に動作させる環境に適しているオープンソース
- Amazon Aurora や Percona Server な ど の 互換 RDBMS や、 MariaDBのような派生製品が開発されている
- OSに対する負荷が小さく、レプリケーション機能が搭載されている
- 複数のストレージエンジンを利用できる
- 他と比べると機能が少ないが8.0.2あたりから機能が増えて他と大差ない感じになってきた
SQL Server
- Microsoft Windowsと親和性が高く、Azureの基盤となっている
- Microsoftが作っている
- Oracleと並んで高機能、Linuxでも動く
- 高度な自動化が特上。ELT機能、レポート機能、分析システムが同時にインストールされる
プロセス構成
- RDBMSが実行されている状態をインスタンスと呼ぶ
- プロセス
- メモリ
- ストレージ
- インスタンスのプロセス構成は、マルチプロセスモデルとマルチスレッドモデル
- マルチプロセスモデルはユーザーからの接続ごとに新しいプロセスを起動する
- 特定のプロセスに障害が発生した際の影響範囲を小さくできる利点がある
- Oracle,Postgreが採用している
- マルチスレッドモデルはプロセス内に複数のスレッドが作成される
Oracle
- インスタンスは複数のプロセスから構成される
- インスタンスとは別にクライアントからの接続を受け付けるリスナーもいる
- インスタンスを維持するための必須プロセス(バックグラウンドプロセス)
- 接続ごとに起動されるプロセス(サーバープロセス)
- 両方独立したプロセス
- バックグラウンドプロセスは相互監視をしており、停止するとインスタンス全体が停止する
- サーバープロセスはSQLの解析、実行計画の作成、実行、結果の送信をする
- サーバープロセスに障害が起きても、トランザクションが自動的にロールバックされ他には無害
- クライアントの接続とサーバープロセスが1対1になる構成を「専用サーバー構成」と呼ぶ
- ネットワーク経由でクライアントからの接続を受け取るプロセスをリスナーと呼ぶ
- 通常ホストに1個
Postgre
- インスタンス全体を管理するpostmasterプロセスと子プロセスで構成される
- 子プロセスには必須のバックグラウンドプロセスが稼働する
- postmasterプロセスが接続を受けると、子プロセス(バックグラウンドプロセス)を作る
- マルチスレッドモデルは提供されていない
- 接続と切断が頻繁に行われるwebアプリなどでは、アプリケーションサーバーのコネクションプーリングを使って接続時のオーバーヘッドを軽減する対策をすることが一般的
- バックグラウンドプロセスが異常終了すると全セッションが強制的に破棄される(影響範囲を小さくするわけではない
MySQL
- インスタンスは単一プロセスとして動作する
- 内部的には複数スレッドが動作する
SQLServer
- インスタンスは単一プロセスとして動作する
メモリ構成
- 一度使用したオブジェクトをメモリ上にキャッシュとして保持して再利用する
- そのため大規模なメモリ領域が必要
- 全セッションで共有するメモリ領域と固有に持つメモリ領域に分割されている
- Oracle,SQL Serverは何でもキャッシュするが、MYSQLは実行結果や実行計画はキャッシュしない
- ソート処理などはセッションを管理するプロセスのローカルメモリに保存
- マルチプロセスモデルはOSの共有メモリ機能を使う
Oracle
- 全プロセスで共有するSystemGlobalArea(SGA)と接続単位に使用するローカルメモリProgramGlobalArea(PGA)がある
- SGAは共有プール、ログバッファ、バッファキャッシュなどがある
- SGA全体のサイズのみを指定して、内部の領域は自動的に行うことをおすすめする
- バッファキャッシュはテーブル、インデックスなどのデータをキャッシュする
- 大きな領域を確保してある
- 共有プールはオブジェクトの構成をキャッシュするディクショナリキャッシュ、SQL分の実行結果を保存するリザルトキャッシュなどから構成される
- ログバッファはトランザクションの情報を一時的んキャッシュする
- PGAはSQL分を実行するために確保するローカルメモリ領域
- 必要なときに必要なだけ確保される
Postgre
- 共有バッファは構成情報、テーブルのデータなどが格納される
- WAlバッファはトランザクションの更新情報を格納する(WriteAheadLog)
- ソート処理などはバックエンドプロセスのローカルメモリで行う
- メモリが足りない場合は一時ファイルを作るので、パフォーマンスが悪化する
MySQL
- 単一のプロセスなので共有メモリは使わない
- 全体で管理するメモリをグローバルバッファ、各セッションが利用するメモリ領域がスレッドバッファ
- グローバルバッファはバッファプール、ログバッファに分類される
- バッファプールはテーブルの情報をキャッシュする
- トランザクション情報を一時的に保存するログバッファ
- ソート処理などはスレッドバッファを利用する。メモリが足りないとストレージ上で実行される
SQLServer
- ローカルメモリのみ
- 構成は最大と最小のメモリを定義するだけ
ストレージ構成
- 永続化とパフォーマンス維持のため、トランザクションログとデータ両方に書きこみをする
- テーブルやインデックス内のデ ータを保存する領域と、更新トランザクションによる差分データ(トランザクションログ)を保存するストレージ 領域を持つ
- 更新が確定(コミット)するとトランザクションログとメモリの情報を同期的に更新する
- データ領域は古いままだが、チェックポイントという機能によりメモリとデータ領域が同期される
- トランザクションログは障害のときに最新にするために使われる
Oracle
- ストレージ領域はファイルシステムを使うこともできるが、最近ではストレージ管理専用のインスタンス(ASM インスタンス)が流行り
- テーブルやインデックスなどの永続データは表領域(TABLESPACE)と呼ばれるオ ブジェクトに保存する
- トランザクションログ(REDOログ)は複数のグループから構成される
- サイズとパスは管理者が決定し変更するためにはグループの再作成が必要
Postgre
- ストレージ領域はファイルシステムと密に結合している
- ファイルシステムの階層構造をそのままデータベースの構造に利用している
- Oracle Database や MySQLでは、レコードが更新されると更新前のレコードはブロック単位にロールバックセグメントに退避される
- 不要になるとロールバック・セグメントから削除される
- Postgreはレコードが更新されると更新前のレコードはそのまま保持され、更新後のレコードは同じブロックに追記される
- VACUUM処理により更新前のブロックは削除される
- すべてのデータはデータベースクラスタと呼ばれるファイルシステム上の単一ディレクトリに保存される
- テーブルやインデックスなどのオブジェクトはそれぞれ個別のファイルで管理する
- ファイル名はオブジェクトを示す一意なoid
- トランザクションファイルはWALファイルと呼ぶ
- ファイル名は自動的に決められる
- すべてのファイルは一定のサイズで、基本16Mで一度作成すると変更ができない
- 原則バックエンドプロセスが書き込む
- 順番に書き込まれいっぱいになったら次のファイルに名前を変えて書き込まれる
MySQL
- 複数のストレージエンジンを使えるのが特徴
- 古いのはMyISAMが使われていたが、最近はInnoDB
- ストレージはデータディレクトリに保存される
- ファイル名やディレクトリは設定で自由に変えられる
- デフォルトでデータベース名と同じ名前のディレクトリができる
- トランザクションログはバイナリログとInnoDBログがある
- バイナリログにはレプリケーションに使用する変更イベントが格納される
- InnoDB ログはトランザクションの完了と同期して書き込みが行われる
- InnoDBはOracleのように予め大きなサイズのDBを作る方法と、テーブルごとに作る方法がある
SQL Server
- 他と比べるとシンプル
- データベース単位にデータファイル を作成する
- トランザクションログは大規模な一つのファイル
SQL を比べる
- SQL 文は標準化の対象にならなかった分野に微妙な差異がある
- ISOやJISの標準化により多くの構文が同じ動きをする
- 固有の構文ができており差別化の要因になっている
関数や演算子の違い
- NULLを変換する関数名がRDBMSによって違う
- 文字列の長さの定義が微妙に異なる
- Oracleは文字の数と定義する
- Postgreは文字数だが、終端の空白は無視する
- MySQLはバイト数と定義する
- MysQL以外は0による割り算を行うと実行時エラーになる
- MySQLは0による割り算はNULLになる
- Oracleの時刻関数はマイクロ秒まではかれる。日付と時刻を返す