Firebaseを使ってアパレル生産用のAndroidアプリを2週間でリリースできた話

ogp

はじめに

MSP技術推進部の基幹化推進チームの中嶋です。

私達のチームでは、マルチサイズプラットフォーム事業(MSP)におけるデジタルトランスフォーメーション(DX)の取り組みを行っています。その取り組みの1つにAndroidを使って、検品結果を記録するアプリの開発・導入があります。

実はこの施策は約2週間で開発されたものです。今回のブログではどうやって短期間でリリースできたのかを紹介します。

開発の背景

検品検寸アプリ誕生のきっかけは、製品の販売前の検品にかかる時間を効率化したいという声からでした。

MSPの全ての製品は、工場が行う検品とは別に弊社が検品会社と協力して検品を行っています。

検品後速やかに弊社側で検品結果をまとめ、製造を担当している商社・工場へレポートする必要がありました。しかし、この工程では作業時間の多くをレポートを作成する業務が占めており、作業効率が大変悪い状態でした。

MSP事業の生産管理・品質管理の担当と話して、この問題はAndroidアプリを使って解決できると分かり、開発がスタートしました。

inspection-general

アプリ開発のアプローチ方法

どのようなアプローチで短納期の開発が実現できたのかを説明します。

システム

このプロジェクトは開発者の人数が限られており、開発に使える時間が短いものでした。それに加えAndroidアプリ開発の比重が大きく、サーバーサイドの開発に工数を掛けられないため開発ボリュームを抑えたいと考えていました。そこでアプリのバックエンドとしてFirebaseを採用することにしました。

下図は開発時に目指した、Androidアプリとシステムが連携した構成です。

final-system

Firebaseを選んだ理由

以下の特徴があるため、Firebaseを選択しました。

  1. 導入のしやすさ

    • モバイルデバイス(Android・iPhone)と連携することを前提としたサービスプラットフォームである点
    • ドキュメントが充実している点
    • 会社がG Suiteを導入していることにより、社員アカウントで開発を始めらる点
  2. 設計・実装時間の圧縮

    • データベースとストレージサービスの両方が利用可能である点
      • Cloud Firestore:NoSQLデータベースで検品・検寸データを保存
      • Cloud Storage:不良画像の保存とアプリ更新配信のために最新APKを格納
    • スキーマレスDBでデータ保存できるという点
      • サービス立ち上げ時に好都合だった
    • SDKが充実しており実装コストが少ない点
  3. 社内の導入実績

    • ZOZOTOWN Androidチームによる導入のサポートが得られる点
    • ZOZOTOWN Androidチームの利用実績がある点

参考:Firebase プロダクト

Cloud Firestoreについて

データ作成

Firebaseで新規プロジェクトを作成すると、利用したいプロダクトが表示されます。

そこで、データベースはCloud Firestoreを選択します。Cloud Firestoreのコレクションは、ボタンを数回押せばデータベースができあがります。

参考:Cloud Firestore

スキーマレスなデータベースのためデータ構成を設計・調整しながら、クライアントとなるアプリケーション開発も別軸で進められます。限られた期間内で開発するような場合に、この柔軟さは工数の短縮に繋がります。

簡単な例ですが、userというデータを考えます。

object user {
  name = "xxxx"
  age = 37
  address = "xxxxxxxxxxxxx"
  telephone = "03-123-9876"
}

格納するデータを設定する画面です。

firestore_create-collection

データタイプも豊富です。オブジェクトの属性は、mapタイプで設定していきます。

セキュリティ

データリソース毎にアクセス制御する仕組みがあり、各データに対するルールを設定することで柔軟なセキュリティルールを指定できるようになっています。

アクセス設定 権限種類
read 読取
write 書込
create 作成
update 更新
delete 削除

参考:基本的なセキュリティ ルール

下の例はCloud Firestoreのリソースへのアクセス制御の「ルール記述」のサンプルです。

service cloud.firestore {
  match /databases/{database}/documents {
    // リソースへのアクセス制限をパスで指定する
    match /コレクション名/リソース名 {
      // リソースへの読み書きを条件付き許可する
      allow read, write: if <条件>;
    }
  }

  // 検品検寸アプリの権限設定は、特定のコレクションに対してはフルアクセスとしている
  match /databases/{database}/documents {
    // *(アスタリスク)はワイルドカードでMatchさせるために指定
    // Inspectionコレクションに対するセキュリティの設定例
    match /Inspection/{Inspection=**}  {
      // リソースへの読み書きを常に許可する
      allow read, write: if true;
    }
  }
}

セキュリティに関しても簡易にかつきめ細かく設定できるという点は安心ができました。

参考:セキュリティの記述方法

Androidからのアクセス実装方法

SDKをimportして実装します。

コールバックは非同期で受け取れるため、アプリケーション側はそれを考慮した実装にする必要があります。 Kotlinでの実装サンプルを下に示します。

  • データ登録
// Firestoreインスタンス
val db = FirebaseFirestore.getInstance()

// 設定
val settings = FirebaseFirestoreSettings.Builder()
    .setPersistenceEnabled(true)
    .build()
db.firestoreSettings = settings

// 登録データはmapで設定
val user = hashMapOf(
            "name" to "User name",
            "age" to 29,
            "address" to "xxxxxxxxxxxxx"
            "telephone" to "090-123-4567"
        )

// 登録処理
db.collection("users")         // コレクション名
    .document("xxxxxxxxxxxxx") // リソースパス
    .set(user)
    .addOnSuccessListener { documentReference ->
        // 成功時のコールバック
        Log.d(TAG, "DocumentSnapshot added with ID: ${documentReference.id}")
    }
    .addOnFailureListener { e ->
        // 失敗時のコールバック
        Log.w(TAG, "Error adding document", e)
    }
  • データ読み取り
db.collection("users") // コレクション名
    .get()
    .addOnSuccessListener { result ->
        // 成功時のコールバック
        for (document in result) {
            Log.d(TAG, "${document.id} => ${document.data}")
        }
    }
    .addOnFailureListener { exception ->
        // 失敗時のコールバック
        Log.w(TAG, "Error getting documents.", exception)
    }

ドキュメントやサンプルなども揃っており、記述をしながらも躓くことなく直感的に実装できました。この実装のハードルの低さも工数削減につながりました。

Android開発

MSP技術推進部のメンバーはAndroid開発経験がなかったため、ZOZOTOWN Androidチームに開発のサポートを依頼しました。うまくコラボレーションするために以下の観点で担当分けをし、それぞれ実装を分担しています。

  • ビュー周りはAndroid独自の概念が多いためAndroidチーム
  • ロジック周りは事業周りの知識が必要になるためMSP技術推進部メンバー

アプリケーション構成

下図のような構成を考えてもらいました。特徴はViewModelActivityは「1対1」です。

MainActivityにぶら下がるFragmentがアプリケーションの表示の中心になります。

app-structure

データ取得から表示までのイメージ

app-data-flow

  1. UIなどイベント発行をFragmentで検知し、ViewModelにアクションを移譲
  2. ViewModelは要求されたアクションを実行しFirestoreにデータをリクエスト
  3. ViewModelがレスポンスを受け取る
  4. ViewModelFragmentにデータへ渡す
  5. Fragmentがデータを表示

Fragmentの実装例を紹介します。

各Fragmentは下のようにViewModelの参照を持ちます。

vm = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)

userListButtonボタンのクリックイベント設定します。ViewModelのgetUserList(ユーザーリストの取得)メソッドを呼び出して非同期処理を設定しています。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  userListButton.setOnClickListener {
    vm.getUserList().addOnSuccessListener { result ->
      // 成功処理
    }
    .addOnFailureListener { exception ->
      // エラー処理
    }
  }
}

次に、ViewModelの実装例です。

FirestoreのTaskオブジェクトを返し、Viewにコールバック処理を移譲します。結果は非同期のため、成否処理を表示側でコントロールしてもらいます。

fun getUserList() Task<QuerySnapshot> {
  return db.collection("users").get()
}

表示は主にFragmentの実装、ロジックはViewModelとそれと連携するModelなどを中心に実装しました。

それぞれの実装だけに注力できたため大きな混乱もなく開発ができました。

画面遷移

アプリの各画面表示はFragmentが入れ替わっているだけです。

inspection-demo

BLEメジャーと通信してアプリに結果表示する

BLEメジャーで計測したその結果をアプリの画面に表示する実装を紹介します。

measurement-process-flow

BLEメジャーとAndroidアプリ

下図が連携しているイメージです。

デバイス同士のペアリングは予め設定しておきます。ペアリングしていれば、入力待ちになっている(フォーカスがある)テキストフィールドに採寸・測定値が自動的に入ってくる仕様です。

ble-measure-app

したがって、連携の実装はとてもシンプルで、テキストフィールドのフォーカスの管理とテキストが入力されたときの処理を実装するだけです。

メジャーからは「採寸値 + キー入力値」が入ってくるため、自動でフォーカスが移動します。

// 実装ではテキストフィールドのフォーカスの移動に処理を入れている
editText.setOnFocusChangeListener { _, hasFocus ->
  if (hasFocus.not()) {
    // フォーカスが外れた時の処理
    //
    // 判定: 採寸値が許容寸の範囲内で仕上がっているかどうかを計算する
    // 結果を表示に反映する
  } else {
    // フォーカスが入ったときの処理は、特に何もせず入力値が入るのを待つ
  }
}

採寸入力デモ

ペアリング中のメジャーから採寸値が入力され、検寸結果が即座に判定されます。

ble-measure-input

配信とアプリのアップデート方法

今回作成したアプリケーションはクローズドなアプリですが、自社以外に配布する必要がありました。さらに日本以外での展開を当初から念頭に入れていました。そのためアプリの配布と更新については、Google Playのストアを経由しない配布とアップデートの仕組みを考慮する必要がありました。

そこで配信に使用したのがFirebaseのCloud Storageです。Androidアプリ側でアプリケーションのバージョンをチェックし、更新の必要な場合はアプリケーションが自動的にダウンロードをするような仕組みを実装しました。

distribution

下図がCloud FirestoreとCloud Storageを利用した更新の流れです。

app-self-update-flow

Androidの仕様上アプリ外に一度処理は流れますが、ユーザー操作数を極力少なくできているため、大きな障壁とはなりませんでした。

まとめ

新規Androidアプリとデータベースの組み合わせを2週間という納期でリリースできたポイントを振り返ると以下の通りです。

  • Firebaseを利用した開発が目指していたアプリサービスの要件と実装の難易度があっていた
    • サーバレス
    • Androidデバイスとの連携
    • アプリケーション開発の実装ハードルの低さ
  • リリースまでの開発でいくつかの同時進行で開発ができたことで工数を削減できた
    • クライアント側の実装を止めることなくバックエンドのデータ構成を調整しながら開発を進められた
    • Android開発で開発スコープを絞った分業実装(表示レイアウトとロジック実装の開発の分割)ができた
  • チーム内外でのサポートが受けられた
    • ZOZOTOWNチームから人員と実装アイデアなどのサポートをもらえた
    • チームからはデータ構成や資料提供などを優先的にしてもらえた

技術的なポイントとチームとしての連携がうまく絡み合い成し遂げられたリリースでした。

成果

「開発の背景」で説明した検品結果の記録と集計作業をシンプルにするため、下図の構成に変更しました。

app-installed

検品検寸アプリ導入前は、製品の検品結果は紙とペンを使って記入していました。記入された紙は現場のマネージャーが集計し弊社にメールで送信、その後メールに書かれた結果を弊社で集約し、サマリーレポートとして作成していました。

導入後、検品会社のオペレーターやマネージャーが行っていた結果の記入・転記・集計作業を自動化することができ、作業全体の効率化に貢献できました。 1つ成果として、検品結果の集計作業では以下のような改善効果が得られました。

improve-result

おわりに

本記事ではMSP技術推進部の取り組みの1つのAndroid検品・検寸アプリの立ち上げについて紹介しました。

ZOZOテクノロジーズでは、ZOZOTOWNやWEARのサービスをはじめ、事業を支えるさまざまな職種を募集しています。ご興味のある方は、以下のリンクからぜひご応募ください!

tech.zozo.com

※「QRコード」は、株式会社デンソーウェーブの登録商標です。

カテゴリー