RoomClip 開発者ブログ

DroidKaigi 2019 で公開!RoomClip デザインガイドラインに関するアプリエンジニアの思惑(そしてDDDとか)

RoomClip のアプリエンジニアをやっている鷲田です。

先日、DroidKaigi で RoomClip のデザインガイドラインを公開し、それについて弊社デザイナーの takaaya が記事を書きました。

ありがたい事に DroidKaigi に参加した多くの方にガイドラインについて興味を持ってもらえました。デザインガイドラインの作成については僕も当事者の一人なので、今回はアプリエンジニアからの視点で考えていたことを書いてみます。一部を除いてエンジニアじゃなくても読めるようにしています (なのでみんな読んで)

RoomClip のデザインガイドラインを作る事になるまで

多くのスタートアップの例に漏れず RoomClip の開発初期1は手探りのまま勢いで作っていた事もあり、デザインをどれだけ忠実に反映するかの合意がかなり曖昧でした。そのため、以下のような事が起きていました。

  • 作成時期や実装者によって品質がバラバラ
  • 過去の(あまりよくない)実装が使い回される

デザイナーや途中から入ってきたアプリエンジニア(自分もその一人)からするとあまり印象の良いものではなく、 「治安悪いわー」などと嘆いていました

ちょうどその頃は Atomic Design が流行り始めていたり Android の Material Design Guidelines がエンジニアの中で広く受け入れられたりしているような時期で、以前よりも「再利用可能なデザイン設計書を用意するべき」という空気になっていました。

そんな中、「RoomClip をリニューアルする」2 という話があり、トップページや検索画面を作り直すという事になりました。デザイナーやアプリエンジニアにとっては 「この期を逃す手はない!」 という事になり、このタイミングでデザインガイドラインが作られ始めました。

デザインガイドライン作ってもうまくいかないのでは?という懸念

ただ、世の中の設計書やアーキテクチャーがそうであるように「単にデザインガイドラインを導入しただけではうまくいかないのでは?」という懸念が個人的にはありました。例えば、以下のような悪いシナリオが想像できます。

  • ガイドラインに合わせられない「例外」が大量発生。そして無法地帯化
  • ガイドラインが無意味にガチガチすぎて開発効率が悪化
  • デザイナーとエンジニアやディレクターとの温度感が違い過ぎてコミュニケーションロスが発生

そういった事態にならないように、例えば以下の事に気をつけながらガイドラインをうまく運用できるためのお膳立てを行っていました。

  • 知識のベースラインを確保する → (プラットフォームの)ガイドライン読み合わせ会
  • ガイドラインを忠実に実装する

ガイドライン読み合わせ会の開催

takaaya さんの記事にもある通りなのですが、RoomClip のデザイナーとアプリエンジニアで Material Design Guidelines と Human Interface Guidelines の読み合わせ会をやっています。みんなで読んで持ち回りで分担して書いてあった内容を毎週発表する、という事をしました。

これには以下の狙いがありました。

1. 知識のベースラインを確保したかった

デザイナーとエンジニアではデザインに対する知識や認識にどうしても大きく違いがあります。そのままだとそれぞれの認識で考えてしまい、ちゃんとしたコミュニケーションが取れません。結果デザイナーが「治安の悪い」実装に甘んじるか、デザイナーの慣れた流儀(WebだったりiOSだったり)にエンジニアが合わせて無理矢理実装する事になります。

これはある程度は仕方がないのですが、同じ指針をみんなが学習すれば、その指針を元に議論を行う事ができるようになります。例えば「このボタンの動きはプラットフォームのガイドラインと異なる動きにする必要がないからそのまま使おう」となったり、「この部分は Material Design には合っていないけど、こういう理由があるのであえて変えよう」みたいな根拠を伝える事ができるようになります。

2. 別の立場の人間が議論するための題材が欲しかった

プラットフォームのガイドラインは実装と表裏一体で、ガイドラインに従う事が前提で開発環境(Android FrameworkやUIKit)が用意されていたり、逆にガイドラインの裏に開発環境の文脈が隠れていたりします。

そういった暗黙的な知識や肌感といったものは開発環境のドキュメントやガイドラインを読むだけでは理解が中々難しいと思います。ガイドラインをたたき台にして立場の違う人達から意見を出し合す事で、お互いの視点をより理解できるようになります。

結果

実は Material Design Guidelines の読み合わせ会に関しては RoomClip のデザインガイドラインを作る数ヶ月前から始めています。なので正確にはデザインガイドラインを作る際に行った工夫ではないのですが、当初からガイドラインを作るようになる事をある程度見越していたように思います。

結果として、ガイドラインを作ろうという話になったタイミングで Material Design に関するある程度の素養は全デザイナ・アプリエンジニアの中で出来上がっていました。そのため RoomClip のデザインガイドラインに関してもその意図や構成に関しても、それほど大きな認識の齟齬は発生せずに実装まで適用できたと考えています。

デザインガイドラインを忠実に実装する

実装する際は、ガイドラインの仕様を写したようなコードになるようにしました。例えば、以下のような事をしています。

  • 色やフォント定義、コンポーネントなどといった要素は(他にやり方がない場合を除き)必ずクラスや xml に定義し、それを経由して使用する
    • 色定義やフォント定義を直指定したりしない
  • 要素の名前はガイドラインと同じにする
    • 大文字やスペースなどの細かい違いはあり

理由としてはコードの可読性がありますが、他には 「ガイドラインに修正が入る可能性がある」 というのも大きな理由です。抜本的でないレベルの仕様変更をある程度見越すのはエンジニアの領域です。なので、ガイドラインにちょっと変更を加えたら表示が崩れたり、直すのに工数がかかり過ぎて変える事ができない、という事態になるのはエンジニア側の問題であり、ちょっと恥ずかしいという思いがあります3

また、「ガイドラインに無い共通化は行わない」ようにしています。例えば、以下のような事は避けています。

  • 似たような要素を複数作るのが面倒なのでガイドラインにない共通化を行う
  • 便利だからといってガイドラインにない要素を独自で考えて共通クラスとして作る
    • (プライベートな共通クラスは作る事があります)
  • (Android で)共通スタイルをテーマに組み込みすぎない

これも「ガイドラインに修正が入る可能性がある」事を見越したためで、不用意な共通化はガイドラインの修正時に意図しない修正が発生したりします。実装の都合で共通化したくなったら、まずはデザイナーに相談します。

結果

それほど頻繁ではありませんが、「ガイドラインに小さな修正が入る」事がありました。例えば全体の行間を変えたり、特定の色を濃くしたりなど。上のように実装していたおかげで、実装側の修正箇所も小さくなりました。

このようにしたトレードオフとして、デザイナーに対してもある程度の実装のセンスを求める事になっています。例えば全く同じ色なのにテキスト色と背景色は分けたりするといった思考が要求されるようになっています。

これに関してはデザイナーがHTML/CSS/JSの実装をある程度理解していたりするので現状問題になっていないように見えます。

上手くいった背景

RoomClip ではガイドラインを作る事によって開発効率をあまり落とす事なくある程度のデザインの品質を確保できるようになりました(と思います)。

上手くいった背景として、デザイナーとエンジニアの距離が近い所があります。デザイナーとエンジニアがほとんどコミュニケーションを取らないような体制だったら、現在のようにガイドラインを調整しながら実装していく事は難しかったでしょう。

おまけ: 背景にはDDD的な発想

エンジニア界隈で有名なシステム設計の思想に「ドメイン駆動設計(DDD)4」というのがあるのですが、上記の工夫は DDD に影響を受けています。例えば、

  • 「コミュニケーションの言葉と実装の言葉を一致させる(ユビキタス言語)」
  • 「チーム全体の知識を育てる(継続的学習)」
  • 「使いこなして直していく事で柔軟なデザインガイドラインを育てる(しなやかな設計)」

などを意識しています。DDD というとユビキタス言語やCQRS/Event Sourceのような、開発プロセスやアーキテクチャーなどの文脈で語られる事が多いですが、今回のように「ドメイン」にこだわらなくてもそこにある基本的なアイデアは色々応用ができるのではないかと思います。


  1. RoomClip のサービス開始は 2012 年です。意外と古い。 ↩︎

  2. 2017 年にリニューアルしています。 ↩︎

  3. でもよくある🙄 ↩︎

  4. エリック・エヴァンスのドメイン駆動設計 ↩︎


この記事を書いた人:鷲田

ルームクリップのアプリケーションエンジニア。


AWS SDK for Ruby V3 Pinpointを試した

お久しぶりです。

ルームクリップ株式会社でエンジニアをしています、仲本です。

当社は、2018-05-02をもちまして、ルームクリップ株式会社になりました。 今後ともなにとぞよろしくお願いいたします。

今回は、RubyのSDKでPinpointを使ったので、ポストしたいと思います。 (ここ最近社内でWEBアプリケーションの言語はRubyで統一しようという流れになっているのもあり)

やること

  1. PUSH通知対象のJSONを作成
  2. S3にJSONをアップロード
  3. セグメントを作成
  4. キャンペーンを作成
  5. PUSH送信

PUSH通知対象のJSONを作成

Pinpointでは、セグメントに登録するエンドポイント(device_tokenとかの事ですね)をJSON形式でS3にアップロードしておいて、それをセグメント作成時に読み込ませる事が出来ます。

サンプルとして次のようなJSONを作成します。

xxxxxxxxxxxにはテスト端末のdevice token(iOS)もしくはregistration id(Android)を入力してください。

test.json

{"ChannelType":"APNS","Address":"xxxxxxxxxxx","Demographic":{"Platform":"iOS","Make":"Apple"},"Attributes":{"userName":["taro"]}}
{"ChannelType":"GCM","Address":"xxxxxxxxxxx","Demographic":{"Platform":"Android","Make":"Google"},"Attributes":{"userName":["jiro"]}}

S3にJSONをアップロード

Endpointを固めたJSONをS3にアップロードする処理を追加していきます。

AWS-SDKを使います

Gemfileに追加します

gem 'aws-sdk-s3', require: false

実際に使うソースで読み込みます

require 'aws-sdk'

s3のインスタンス生成

s3 = Aws::S3::Resource.new(
  region: 'us-east-1',
  credentials: Aws::Credentials.new('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')
)

JSONをアップロード

こんなエンドポイントになる想定
s3://YOUR_BUCKET_NAME/pinpoint/test.json

file_path = 'test.json'
key = 'pinpoint/test.json'
File.open(file_path, 'rb') do |file|
  s3.client.put_object(bucket: 'YOUR_BUCKET_NAME', key: key, body: file)
end
's3://YOUR_BUCKET_NAME/' + key

セグメントを作成

Pinpointインスタンス生成

pinpoint = Aws::Pinpoint::Resource.new(
  region: 'us-east-1',
  credentials: Aws::Credentials.new('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')
)

create_import_jobでセグメント作成リクエスト

create_import_jobをすると、AWSコンソールのセグメント一覧にYOUR_SEGMENT_NAMEとして作ったものが出てきます。今回のサンプルは小さいのですぐにインポートが完了します。

pinpoint.client.create_import_job(
  application_id: 'YOUR_APPLICATION_ID'
  import_job_request: {
    define_segment: true,
    format: 'JSON',
    register_endpoints: false,
    role_arn: 'arn:aws:iam::xxxxxxxxxx:role/xxxxxxxxxx',
    s3_url: s3_url,
    segment_name: 'YOUR_SEGMENT_NAME'
  }
)

パラメータ

  • application_id
    PinpointのProjects一覧にIDが記載されています
  • define_segment
    trueにしてセグメント取り込みで新規作成します
  • format
    JSONとCSVが選べます。今回はJSONです。
  • register_endpoints
    取り込みと同時にエンドポイントを保存する場合はTRUE
  • role_arn
    S3に読み書き出来るARNを指定
  • s3_url
    先程アップロードしたS3エンドポイントです。
    s3://YOUR_BUCKET_NAME/pinpoint/test.json
  • segment_name
    セグメントに付ける名前

キャンペーンを作成

セグメントが完成したら、セグメントIDを使ってキャンペーンを作成します。

resp = pinpoint.client.create_campaign(
  application_id: 'YOUR_APPLICATION_ID',
    write_campaign_request: {
      holdout_percent: 0,
      is_paused: false,
      limits: {
        daily: 100,
        maximum_duration: 60,
        messages_per_second: 400,
        total: 1000000
      },
      message_configuration: {
        default_message: {
          action: 'OPEN_APP', # accepts OPEN_APP, DEEP_LINK, URL
          body: '{{Attributes.userName}}さんにお知らせ♪'
      },
    },
    name: 'YOUR_CAMPAIN_NAME',
    schedule: {
      frequency: 'ONCE', # accepts ONCE, HOURLY, DAILY, WEEKLY, MONTHLY
      is_local_time: false,
      start_time: (Time.now + 1.minutes).iso8601,
      timezone: 'UTC+09'
    },
    segment_id: 'YOUR SEGMENT ID',
    segment_version: 1
  }
)

パラメータ

  • holdout_percent
    PUSHを送信しない確率
  • is_paused
    停止状態でTRUE
  • limits
    • daily
      1日に送信出来る件数
    • maximum_duration
      キャンペーン終了後に送信出来るようになる時間(秒)
    • messages_per_second
      秒間送信数
    • total
      最大送信数
  • message_configuration
    PUSH通知の内容を定義。APNS、GCMなど細かく分ける事が出来ますが、今回は最低限。
    {{Attributes.userName}}はテンプレート変数で、最初に作ったJSONに指定した、メッセージ毎のパラメータ。
  • schedule
    • frequency
      1ショットなのか、どういう頻度で繰り返すのか
    • is_local_time
      時刻のローカライズを行うか
    • start_time
      送信開始日時(iso8601)
      今回は1分後に設定
    • timezone
      タイムゾーン
  • segment_id
    先程作成したセグメントID
  • segment_version
    セグメントに付けられるバージョン。
    抽出軸は同じだが抽出するタイミングで変わる類のものの時に使えそう。

PUSH送信

各々のスタイルでPUSHを待つ。

実際にやってみて

  • SDKの仕様書ではパラメータの仕様が細かいところまで理解しにくかったため、REST APIの仕様書をよく使いました。
  • create_import_jobが完了するまで時間がかかる場合があるため、キャンペーン作成前にwaitした方が良さそうです。

以上となります。ありがとうございました。

参考


この記事を書いた人:仲本

ルームクリップ株式会社のエンジニア 社内SE・サーバーサイド・インフラ周りを中心に担当。 味のあるオッサンを目指して頑張っている。 夜は酒場に居る事が多い。