Life is Really Short, Have Your Life!!

ござ先輩の主に技術的なメモ

Flutter楽しいれす

前職の兼ね合いで、一緒になって作りたいサービスを5月から合間見て作っております。

それにはスマホのネイティブアプリが必要だったので、Flutterで作り始めているのですが、これはすごいっすね。ありがとうFlutterを作ってくれたネ申エンジニア。儲かったら334ドルはコミュニティに寄付したい。

初めてiPhoneアプリ+SwiftとAndroidでアプリを作った時、ひとりで全部組んだせいもあって、だいたい半年かかった。Flutterで作ったら概ね3週間で実装できた。スマホアプリを作ったことがなくてゼロベースでやったとしても、1ヶ月半もあればできたと思う。

Flutterだと生産性があがるいくつかの理由

当たり前だけど、iOS / Androidのアプリが1つのコードに共通化できる。そんなことが可能になる時代になるなんて思ってなかった。Flutterのエンジンがどういう理屈かわからないけど、Flutterで実装すると、Xcode / Android Studioのプロジェクト形式にコードを書き出してくれる。Flutterのコアはコードジェネレーターなのだ。コードが共通化できることで、車輪の再発明がなくなった。使用するライブラリのメンテナンスコストが下がった。iOSAndroidで別々に管理しなくて良くなったからだ。

状態管理も楽になった。Androidの場合はActivityやFragmentのライフサイクルに気を使ったりして面倒だったり、iOSもViewdidLoad/ Appear系の関数をうまく使って状態管理をしていた(そういうコードを書いてしまったのだ)が、Flutterになると原則「StatelessWidget + Provider」でやっていけるので、コードの記述量も減った。

何よりも嬉しいのは、豊富なWidgetがあること、ビューがコードで書けること。iOSのStoryboardや、Androidのlayout_xx.xmlとか面倒くさいだけだったな・・・と噛み締める。iOSの場合は、共通化したいViewはxibファイルを作って、親となるViewControllerでaddSubviewしてからのイベントハンドリングはdelegeteかまして任意の誰かにイベントハンドラを実装させて・・・みたいなコードを頑張って書いたが、Flutterの場合はWidget作って返すだけ。カスタムセルとかレイアウトもGridからColumn/Row、スクロールなども簡素化できた。ネストが深くなるけどね。状態が変更されたらWidgetがリビルドされることで、モデルのデータに沿ったビューが簡単に実装できるのも良い。

ネイティブだと、ビューの癖が強いのも生産性を下げる一因だったんだと思いました。iOSのUITableViewとかは、複数のオーバーライドしないといけなかったり、セルをカスタムする場合はカスタムセルのXib作って、AutoLayoutで制約与えて(これから解放されたのもでかい)... そうなんですよ、iOSってビューを作るのがだるいんですよ。Flutterになって、ビューを作るのが楽しくなった。全部コードで組めるからだと思う。

Flutterが重くなるとすればビルドしまくることなんだろうな... あまり想像できないけど。

リーチ一発Flutterでいけるか?

いけると思う。XCodeで開発したことがなくても、FlutterのWidgetのライフサイクルや、BuildContextの思想などをある程度理解できれば良い。AndroidiOSのビューのライフサイクルに熟知しないと作れないことはないかな。問題があるとしたら、Flutterでジェネレートしたネイティブのコードに対する修正が必要な場合だけど、よっぽどのことがない限りないと思う...

今後スマホのネイティブアプリを新規に作るとしたら、Flutterが良いですよ。はい。

jqueryでappendした要素に対するイベントが必要なら、onを使おう

当たり前だけど、appendで後からつけた要素にはイベントは付与されない。onloadで指定してそのままだからね。

が、jqueryのon関数を使うことで、動的にイベントをバインドしてくれる。あざーす。

だめなやつ

$('.img_cancel_btn').click(function() {
    $(this).closest("div").remove();
});

ナイスなやつ

$(document).on("click",".img_cancel_btn", function() {
    $(this).closest("div").remove();
});

要件定義はアートなのでは

要件定義が死ぬとシステム開発無理、なので要件定義重要。それはそうなんだけど、上手くやるには人類には難しすぎた。要件定義はアートなのではなかろうか。再現性に乏しく、経験値や個々人のアンテナの感度によって、成果がブレる。それに、クライアントにも左右される。要件定義の支援に入っても、そこで揉める。

真っ白いキャンパスがあって、そこにシステムを使った理想の世界的なものを書くわけだけど、そこにフォーマットや方法論が与えられたとしても、とりあえず何か作ったほうが100倍響くわ。ダラダラとそんなことを思ってたら、「SIやりてぇな〜」って気持ちが強くなってきちゃった。IT企画という翼をさずけたら、プログラミング経験が乏しくても、やっていけるもの。

SIとなると、SaaSじゃだめでPaaSが必要になる。そうなると、中小企業でも担げるのはkintoneしかない。安いし、拡張性高いし、運営は盤石だし、国産だし。あ●としすてむずとか高すぎて無理。サーバー立ててオンプレで入れるみたいなのも、ユーザー目線では重たく見えるだろう。

kintoneはリリース当初はこれは無理だろと思って横目に見ていてたんだけど、昨年から使ってみると「もうこれでいい、80点なら音速で出来る」と確信。それを100点にしようとしたらイニシャル10倍になりますけど、やりますかって。

kintoneでもなんでもそうだけど、辛いのは在庫の引き落としのように、裏でデータ連携をしないといけないロジック。入力値格納以外のロジックの数が多ければ多いほど、複雑になる。入力値格納以外のビジネスロジックが必要な場合、結局そこでトランザクションが入ってプログラミングしなくちゃいけないし、RDBに入れたいならばCDataとか使って、SQLゴリゴリ書けばいいだけだ。

クラウドの時代になってXaaSが増えれば増えるけど、結局SIが必要になる。導入企業の業務課題は千差万別だから、その隙間を埋めるのはSI以外の方法が存在しない。業務系のようなお金をかけたくないがデータが流れる仕組みはちゃんと作りたい領域において、kintoneのようなPaaSを使って、周辺システムはETL/API連携などでうまいこと組み合わせて、プログラミングの楽しさをプログラミング・レスで提供するSIerって、やっべーすげー面白そう。

www.joyzo.co.jp

Facebookで流れてきたけど、2010年から専業ってすごい。その頃はAccess以下のものだったはずなのにね。でも、後追いするSIerがすげー少ない気がする。ネット上では、個人ベースの発信かエバンジェリストというステマの声しか拾えない。多分、使いこなせないんだよね。使い方を体系立てて教えてないから。おれ、それ教えられるしSIもできるんで、そっちやるわ。

この他に実はもう1個挑戦したいものがあって。ECなんだけどね。あー楽しい。事業計画練るぞ〜。

kintoneに入門した学習メモ

kintoneはようやっとる。

基本のき

  • RDB感覚でいえば、1テーブル=1アプリ
    • RDBと違うのは、子テーブルをN件持つことが可能なので、親子関係を含めて1つのアプリになる
    • 注文を親に持つ注文明細並びに発注明細などを持てる。
  • 入力値に対する関数が少ない
    • SUM, YEN, DATE_FORMAT, IFぐらいしかない
  • 関連するレコードは全部持ってこれる
    • 得意先を親にして、見積明細、受注明細、契約明細、請求明細など、全部持ってこれて、結合条件やソート条件も指定可能。
  • 画面の融通は効かない
    • 例えばボタンだけを置いて他のアプリに画面遷移する、みたいなのができない
    • フォームの画面ではそもそも任意のボタンはおけない
    • 画面遷移はkintoneの仕組みを使って実現するしか無い
  • 親子関係の表現は楽勝だが、孫まではさすがにむり。
  • アプリ間でフィールドのデータコピーが出来る
    • 見積と見積明細をコピーして、契約と契約明細に値をコピーできる
    • なぜか、ルックアップしたやつは引き直しをしないとだめ
  • ルックアップ先の検索条件、原則あいまい検索になってるの助かる
  • ルックアップは値のコピーであって参照のコピーじゃない
    • なので、参照元が100→101に変わっても、追従して変更されない。
    • 得意先コードや品番のような、JOINするキーに該当する値の変更は出来ないようにマスタ管理で縛る。

プロセス管理

とても良く出来ていて、複数の人にお伺いを立てることできるし、条件で分岐することも出来る

  • 基本は「ステータス」を作ること
    • 作成中、上長決裁、部長決裁、社長決裁、承認みたいな感じで。
    • ステータスが変わる条件によって、誰にお伺いを立てるかを設定する
    • 金額が1万未満の場合、作成中→即承認。
    • 1万〜5万は、作成中→上長決裁→承認
  • 決裁担当者は複数人選択できるし、自分で選ぶことも出来る
    • 誰か1人でも承認すればOKもあれば、全員承認しないと駄目、も設定できる
    • 選べる作業者の自動設定は無理っぽい。
    • 自分とメンターみたいな関係があった場合、自分のメンターを自動で引っ張るのは無理。

アクセス制限

  • レコード単位及びフィールド単位で制限ができる
    • 他の担当者のレコードは一切見せたらあかんで、みたいなやつ
    • フィールド単位ってことは、金額がN円で担当者のグループがこれで..みたいなのも出来る
    • ディシジョンテーブルに近道なし。頑張りましょう。

通知

  • この3パターンで通知を飛ばせる
    • アプリのデータが操作されたとき
    • アプリのデータが特定の条件を満たしたとき
    • アプリの日時項目の日時が過ぎたとき

標準では出来ないYOUたち

イベントハンドリング
  • プルダウン変わったら連動して子供をかえる
  • フィールドの値イベントのフック
    • 計算するフィールドの場合はchangeイベントが走る
    • 計算しないフィールドの場合は、自分でonchangeでフックして頑張る
  • 明細グループの中で一意にする
    • 注文明細で品番がかぶったらエラーにしたいとか
  • ルックアップしたレコードをN件挿入する
    • 商品を10個いっぺんサブテーブルに登録ができない
  • ルックアップのオートコンプリート(いうほど使わない)
  • アプリ間連携
    • 注文入れたら商品の在庫減らす的な
    • 注文入れたら顧客の与信額減らす的な
    • 整合性を取るために、リビジョンを利用して楽観的ロックを行う
      • データ取得時とデータ更新時でリビジョン違ったら例外発生でやり直し。
  • 自動採番
    • JSで自分でMAX値を取りに行って++せなあかん
  • フィールドの表示・非表示
    • JSでコードを書く。
  • 帳票出力(開発)
  • ログインユーザーの取得
    • 自分の名前をユーザーをルックアップした時に初期値に入れる、が出来ない
  • お知らせの権限設定
    • ポータルのお知らせは権限設定ができない。スペース内のスレッドでやるしかないみたい。
  • 関連レコードの集計
  • 関連レコードをcsvに出す

入力チェックが弱い

標準で可能なチェックはこれぐらい。弱い。

  • ユニーク制約
  • 必須
  • 文字数制限
  • 数値の型チェック
    • 数値以外が入ったらエラー
    • 最小値と最大値の範囲外ならエラー

見たところ、こういうのは全部頑張るしか無い。プラグインを活用するようだ。

  • 大小のチェック
    • 開始日と終了日で、終了日<開始日ならエラー
  • フォーマットのチェック
    • メアドとURLしかないらしい
  • 相関チェック
    • Aにチェックが入っているなら、項目Bは絶対に入力して貰う必要がある
  • かなやカナ、半角全角のチェック

見れば見るほどよく出来ている。kintone大好きマンになりそう。

内製を支援するビジネスに再挑戦

会社を作った当初はそれを目指していたけど、しっくり来るツールがないのと色んな出会いがあってその方向性はトーンダウンしたが、内製を支援する方向に舵を切ろうと思う。PoCが増えてきたのもある。内製出来る規模のシステムを育てていくのが楽しかったし。

コーポレート・エンジニアリングは、内製が基本だと思う。もちろんSaaS・PaaSはあるだろうけど、結局それらをつなげていかねばならないし、そこに特化出来るように業務を変えていくことになりそう。色んなPaaS・開発ツールを触っては試してみたけれど、自分の中でどうやって組み合わせて行きていけばいいか、ピンときていない。

自分の中で再評価しているのがFileMaker。これ、もしかしてすげーやつなんじゃっていう気持ちがどんどん出てきた。明日から、自分で作った販売管理の仕組みをコイツで作ってみて、やれるところまでやってみようと思います。

Accelerate Corporate Engineering.

SQLAlchemyのLazyLoadとEagerLoad

SQLAlchemyには、カラムを指定できるwith_entitiesという関数がある。

こいつを指定してクエリを組み立てるときは、joinによって指定されたテーブルの中身を、予めもってきてくれるようだ。AlchemyのDebug=Trueで確認した。

 db.session.query(Item).join(Category).with_entities(
    Item.id,
   Category.name
  ).all() 

だけど、もちろんbackrefの指定にもよるけれど、カラムをな~んにも指定しない場合は、lazyloadになる。該当のプロパティにアクセスされたと同時に、SQLが走る。

 rs = db.session.query(Item).join(Category).all()
 for v in rs:
    print(rs.category.name) # この度にSQLが走るっぽい

それは困るぜってときは、予めoption関数でjoinした時にもってこいやっていう指定ができる。

 rs = db.session.query(Item).options(joinedload(Item.category)).all()
 for v in rs:
    print(rs.category.name) # DO NOT N+1 !!

docs.sqlalchemy.org

SQLAlchemy 101って本が日本語であれば、1万円でも買います。

追記 2019.12.28

そんなことしなくても、relationship関数のlazyキーワードに、joinedって入れたらそれだけで良いっぽい

# これでJOIN時にFetchしてくれるからN+1にならへん
user = relationship("User", lazy="joined")

レッツゴーFlutter

展示会運営のアプリをAndroidiOSで各々ネイティブで作って運営しているが、やっぱりワンソースでいい感じにやりたい...2つ同じもの作るのは単純に辛い。メンテナンス・コストが単純に倍になるのがだるい。1から勉強したってのもあるけど、フルコミットでリリースできるまで半年近くかかった。

利用しているライブラリの最新化、OSバージョンアップに伴って廃止/非推奨になったコードのメンテ、ルックアンドフィールの変更に伴うメンテ(今回のダークモードみたいなやつ)、テストコードのメンテ、ビルドシステム(Carthage / Gradle)のメンテ... Webアプリのほうが楽かも。UIのロジックとビジネスロジックの切り分けも気をつけないとすぐ肥大化するし。

Flutterが Android/ iOSクロスプラットフォーム開発の本命だと思われるので、こいつを勉強してなんとかワンソースで頑張りたいンゴ... Google Waveのようなことにはならんやろ。頼むで。