やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

卒論の時に作ったアプリケーションのアーキテクチャを考え直してみた

僕は卒論でVB.netを使って「センサーで計測したひずみ値を描画・記録するアプリケーションの作成」を行いました。学部では土木・建築を専攻しましたが、先生が「プログラミングを極めたい」という僕の気持ちを汲んでくれたことを今でも感謝しています。目的を持ってプログラムを書くことがとても楽しかったです。

ただ、当時の自分には設計や責務を分けるという考えはなかったので、今になって思うと完成したプログラムはひどいものだったと思います。卒業後、あのアプリケーションが保守されているのかは分かりませんが「今の自分が同じアプリケーションを作るならどう作るかな?」と考えてみました。当時の問題を振り返りながら、アプリケーションをリアーキテクチャしていこうと思います。

当時の設計について

ぶっちゃけ、設計もなにもありません。以下の順に処理が進んでいくだけです。

  • 画面の初期描画を行う
  • センサーとの疎通を確認する
  • 一定周期でセンサーの計測値を取得する
  • 画面に描画する
  • 動的配列(可変長配列)に計測値を追加する
  • 計測停止後、計測結果を記録したcsvファイルを作成する

※Rustと混同してVectorと記載していますが、正しくは動的配列です。

問題と改善点

目的の「センサーで計測したひずみ値を描画・記録する」ことは十分に可能で、ファイル保存も可能とは...。
「よくできるなぁ」とまずは当時の自分を褒めてあげたいと思います。その上で、この設計の問題と改善点を考えてみたいと思います。

  • 各工程の処理が1つの処理にまとめられている
  • 計測時間が長くなればなるほど、アプリケーションが重くなっていく
  • 無限に計測できるようで計測できない
  • 途中でクラッシュすると計測結果が記録されない

一番の問題は各工程の処理は疎結合しており、新機能の追加や機能変更、バグの発生箇所の特定が難しく、変化に弱い構造になっているという点です。責務も分かれておらず、広大なスコープを持つ変数と1000行を超えた巨大な関数が出来上がってしまっていました...。

また、計測結果を次々に動的配列に追加するため、計測を停止するまで無限に動的配列への追加が行われます。
結果的にメモリ容量を食い潰してしまうため、処理が進むにつれて段々とアプリケーションが重くなっていきます。計測を停止するまで、まるで無限に計測ができるように見えて、実際はできないという点ではUXが悪いアプリケーションだったとも言えます。

いざ、リアーキテクチャ!

ということで、以下の問題点を解決するためにリアーキテクチャしてみたいと思います。

  • 各工程の処理が1つにまとめられている(疎結合で変化に弱い構造)
  • 計測時間が長くなればなるほど、アプリケーションが重くなっていく(動的配列への無秩序な追加)

構成と処理の流れについて

処理の流れ

  • 立ち上げ時に計測(Measurement)・保存(Saver)・描画(Viewer)プロセスと計測後の処理を管理するプロセス(Manager)をそれぞれ作成
  • 計測結果をQueue(キュー)に追加する
  • Queueの状態をプロセス(Manager)が監視。値が追加された際にイベント処理が発火
  • イベントに登録された保存処理(Saver)が実行される
  • イベントに登録された描画処理(Viewer)が実行される

改善点

どんなアーキテクチャにも必ずトレードオフがあります。
今回は改善のために、以下のアーキテクチャを選択しましたが、パフォーマンスは従来のものよりも劣っているかもしれません。これは書籍「ソフトウェアアーキテクチャの基礎」でも何度も説明されている「トレードオフ」であることは間違いありません。

責務の分割

まず、巨大な1つの関数となっていた処理をそれぞれの責務(プロセス)へと分割しました。
計測後の処理を管理するプロセス(Manager)以外は他の処理が何をするを知りませんし、知る必要もありません。
こうすることで、お互いの処理が依存関係にならず、機能の追加をしたければプロセス(Plugin)を追加すれば良いですし、機能変更もプロセス内で完結します。またコードも分割されて巨大な関数とグッバイすることが出来ました。

責務の分割に伴って、計測結果の一時的な記録には動的配列ではなくQueueを選択しました。
計測プロセスはただQueueに追加するだけで、その後、どうなるか知りません。Queueを監視しているプロセス(Manager)側で値が追加されたことを確認した時に、イベント処理が発火して保存と描画処理が実行されます。

アプリケーションを重くしない

この問題は動的配列に無限に計測値を追加ことで、メモリを圧迫してしまうために発生していました。
ただ、先ほど説明したように計測結果の一時的な記録にQueueを使うようにしたので、プロセス(Mangaer)側で値の取得が行われればQueueのサイズは減ります。こうすることで以前のように無限に要素が増えていくという問題を防ぐことができます。

ただ、プロセス(Manager)や保存(Saver)・描画(Viewer)に何かしらの問題があった際にQueueが詰まってしまう問題が考えられます。今は特に制限をかけていませんが、Queueのサイズに上限を設ればこの問題を回避することができますし、プロセス(Manager)などに問題があった際にプロセスを再起動するなど...対処方法はいくらか考えられます。

実装してみた

ということでVB.netではありませんが、リアーキテクチャした簡易なアプリケーションを作ってみました。
開発にはelixirを選択しました。選定理由としてはプロセスを気軽に作って、相互にプロセスでデータのやりとりをするのがelixirが強みとする点だからです。とても簡単に書けたので驚きました。

github.com

今回はelixirに関する詳細は割愛しますが、Supervisorを使うことでプロセスのライフサイクルも簡単に制御することができます。今回は、プロセス(Manager)に何か問題が発生した時は、配下のプロセスを全て再起動するように設定しました。

最後に

いかがでしたでしょうか。
今回は僕が大学生の卒論で作成したアプリケーションのリアーキテクチャを行ってみました。
こうして改めて考えてみると、当時の自分はまだまだですね...。そんな状態でイキっていたので「すいません...」という気持ちに何故か今になってなりました。

今回、選択したアーキテクチャも改善すべき点があるかもしれませんが、5年の時を経て、少なからず自分の成長を実感できたのは嬉しかったです。皆さんも過去に自分が作ったアプリケーションをリアーキテクチャしてみると何か発見があるかもしれません。ぜひチャレンジしてみてください。

少しでも「ええな〜」と思ったらイイネ!・シェア!・はてなブックマークを頂けると励みになります。