はじめに
この記事では、バックエンドエンジニアを目指す実務未経験者が、ポートフォリオとして作成した Web アプリケーションを紹介します。
自身の振り返りも兼ねて、アプリケーションの概要や使用した技術についてまとめましたので、同じような境遇の方々にとって、少しでも参考になる点があれば嬉しく思います。
アプリケーションの概要
アプリケーション名:「 お薬日記 」
「お薬日記」は、服用したお薬の記録をサポートする Web アプリケーションです。
主な使用技術は、Spring Boot (Kotlin) / Vue.js / AWS ECS (Fargate) となります。
以下の URL で公開しておりますので、よろしければ実際に操作してみてください。
▼ アプリケーション URL
▼ GitHub URL
https://github.com/hiroe-mkk/medicine-diary-webapp
機能一覧
お薬日記では、以下のような機能を提供しています。
■ メイン機能
-
服用記録追加機能
- 服用記録の一覧表示 (薬・日付・ユーザーなどによるフィルタリング)
- 服用記録のカレンダー表示
-
薬情報登録機能
- 薬の画像のトリミングとアップロード
- 在庫情報の設定 (服用記録追加に伴う在庫数の自動更新)
- 薬の一覧表示 (症状による検索)
-
共有機能
- ユーザー検索
- 共有グループへの招待
- 薬情報並びに服用記録の公開・非公開設定
■ その他
-
認証機能
- ソーシャルログイン (初回ログイン時にアカウント自動作成)
-
プロフィール設定機能
- プロフィール画像のトリミングとアップロード
-
お問い合わせ機能
- お問い合わせ内容の確認メール送信
■ 画面イメージ
ホーム画面 | カレンダー画面 | 設定画面 |
---|---|---|
薬一覧画面 | 薬詳細画面 | 服用記録モーダル |
---|---|---|
■ 操作イメージ
閲覧 | 服用記録追加 | 共有グループ招待 |
---|---|---|
※ 共有中はユーザーを意識した UI デザインとなっています。
共有していない場合 | 共有している場合 |
---|---|
作成背景
コンセプト
私は長らく薬が欠かせない生活を送っているのですが、その中で医師から薬の効果や症状の変化を継続的に記録することの有効性を教えていただきました。
こうした記録は、特定のパターンやトリガーを把握し、より効果的な治療法や対策の発見に役立つのだそうです。
アドバイスに従って記録を継続し、効果も実感しているのですが、手書きでの記録は後から見返しにくく、外出時にも不便を感じていました。メモ帳アプリや日記アプリも試しましたが、そのような記録に特化していないため、手軽に入力できなかったり、後から見返しづらかったりと使いづらさを感じていました。
そこで、薬を服用したときに簡単に記録でき、ストレスなくそれらを見返すことができるような服用記録アプリケーションを作成することにしました。
ただ、この機能だけでは魅力に欠けますし、利用シーンも限定されてしまうので、以下のような機能も実装することにしました。
■ 薬の詳細情報を登録する
季節的に服用する薬や使用頻度の低い薬は、用量や用法を忘れがちです。
そのような場合、インターネットで検索することになると思いますが、薬はカタカナが多かったり、微妙な違いがあったりして、案外調べるのに手こずってしまいます。
また、効果や副作用には個人差があるため、そういった情報は自身で管理するしかありません。そうした情報を一元管理し、必要な時にすぐに薬を判別できれば便利なのではないかと考え、薬の詳細な情報を登録できるようにしました。
■ 在庫情報を管理する
風邪薬や痛み止めなどの常備薬は、家庭内で共有されることも多いかと思います。
そのため、在庫管理が曖昧になり、必要なときになかったり、有効期限が切れていたり、なんてことが起こりがちです。
そこで、薬の情報と併せて在庫情報も管理できるようにすることで、いつでもどこでも手軽に確認できるようにしました。
■ 薬情報や服用記録を家族やパートナーと共有する
体調が悪いと、気分がイライラしたり、家事や育児など、いつものことが辛く感じてしまうものだと思います。
しかしながら、いつものことだから、相手も疲れてるだろうからと、家族やパートナーにわざわざ伝えないという方も多いのではないでしょうか。
そのような時でも、お互いの状態が簡単に確認できれば、先に帰って家事をしたり、相手を気遣ったり、支え合える関係を築くことができるのではないかと思い、家族やパートナーと情報を共有できる機能を取り入れました。
方向性とアプローチ
コンセプトを決めるにあたり、主に以下の 3 つの点を重視しました。
1. 個人利用に特化したアプリケーション
2. 自身をターゲットユーザーとする
3. 継続的に改善を続ける
1. 個人利用に特化したアプリケーション
アプリケーションのコンセプトを検討する初期の段階では、多くのポートフォリオ紹介記事で目にする SNS アプリケーションの開発を考えていました。
しかし、SNS アプリケーションは、ユーザー同士のコミュニケーションが主要な特徴であり、サービスとして成立させるには集客が大きな課題となります。
また、プライバシーやセキュリティ、不適切なコンテンツの取り締まりなど、設計段階から運用に至るまで多くの懸念事項があります。
私の現在のスキルで「できる限り価値のあるサービスを提供しつつ、ユーザーの安全性は最優先したい」という考えがあったため、以上を考慮して個人利用に特化したアプリケーションを作成することにしました。
2. 自身をターゲットユーザーとする
アプリケーションが解決する課題を決定するにあたり、自分が欲しいと思う機能を中心に据えることを意識しました。
これは、ニーズをより深く理解し、要件を明確にするため、そして、開発の方針を立てやすくするためです。
一方で、類似するアプリケーションの調査を行い、積極的に周囲の意見を取り入れることで、機能が偏ることがないように心掛けました。
3. 継続的に改善を続ける
このアプリケーションは、自身のスキルアップに合わせて新しい機能や技術も導入していく予定です。また、今後も積極的にユーザーのフィードバックを取り入れて、改善していきたいと考えています。
そのため、保守性や拡張性を確保することを目的に、効率的なコード管理やドキュメント管理、CI/CD などにも力を入れることにしました。
※ 認証機能について
このアプリケーションでは、ソーシャルログインのみを提供しています。
パスワード認証を採用しなかった理由は、セキュリティ上のリスクが挙げられます。
一般的に、パスワードは複数のサービスで使いまわされる傾向にあるため、万が一パスワードが漏洩した場合、他のサービスにまで被害が及ぶ可能性が非常に高いです。
一方で、ソーシャルログインは、パスワードを管理するリスクを外部化し、大手プラットフォームの堅固なセキュリティ対策を活用できるため、セキュリティリスクを低減させることが可能です。また、ユーザーにとっては、数クリックで簡単にログインできるというメリットもあります。
そのため、ソーシャルログインはユーザーの利便性とセキュリティのバランスが取れた認証方法であると判断し、採用しました。
使用した技術
バックエンド
- Kotlin 1.8.2
- Spring Boot 3.1.2
- Spring Security (spring-boot-starter-security) 3.0.6
- Spring Boot Actuator (spring-boot-starter-actuator) 3.1.0
- Flyway 9.21.0
- MyBatis (mybatis-spring-boot-starter) 3.0.0
- Thymeleaf (spring-boot-starter-thymeleaf) 3.0.6
- JUnit (AssertJ) 3.23.1
- SpringMockK 4.0.2
- Gradle 7.6.1
言語
プログラミングに興味を持ったきっかけが Java ということもあり、愛着を感じていたため、はじめは Java を検討していました。
しかし、新しい言語を学習したいという気持ちがあったため、Kotlin を採用することにしました。
Kotlin を採用する決め手となった点は、以下の通りです。
- Java と非常に高い互換性を持つこと
- オブジェクト指向と関数型のスタイルの両方で使用できること
- より現代的で効率的な方法でコードを記述できること
- コードの可読性やセキュリティ、生産性が向上すること
フレームワーク
フレームワークには、主に以下のような理由から Spring Boot を採用しました。
- Java ベース の Web アプリケーションの開発において、デファクトスタンダードであること
- jar 単独でも Web アプリケーションが動作するため、クラウドとの相性が良いこと
- ドメイン駆動設計の実装をサポートする機能が多く提供されていること
- 必要に応じて容易に拡張できること
また、セキュリティを強化し、脆弱性を軽減するために Spring Security も併せて導入しています。
Spring Security は、認証や認可に加えて CSRF や XSS などの攻撃からアプリケーションを包括的に保護するセキュリティ機能を提供しているため、高度なセキュリティ機能を効果的に導入することができます。
O/R マッパー
O/R マッパー には、 MyBatis を採用しました。
MyBatis は SQL マッパーであり、SQL クエリとオブジェクトの直接のマッピングを行うという特徴があります。
そのため、今回採用しているドメイン駆動設計の原則に従ったリポジトリを実装するにあたり、ドメインモデルとデータベースの間の疎結合性を確保できるという点で適していると考えました。
なお、SQL マッパーには DOMA などもありますが、SQL を直接記述できること、公式ドキュメントに Spring Boot に関する詳細な記述があることが決め手となり、MyBatis を採用することとなりました。
マイグレーション
マイグレーションツールに関しては、Flyway を採用しました。
Flyway を選んだ理由は、Spring Boot との相性の良さにあります。
Flyway が組み込まれた Spring Boot アプリケーションでは、起動時にデータベーススキーマの自動マイグレーションが行われるため、アプリケーションのバージョンとデータベースの整合性を維持することができます。
さらに、本番環境 (AWS ECS(Fargate) + RDS) へのデプロイ時にも、アプリケーションの起動に伴って、データベーススキーマがマイグレーションされるため、本番環境への展開をスムーズに進めることができる点もメリットです。
静的リソース
現時点では、アプリケーションはシンプルで規模が小さく、トラフィック量も限られています。
そのため、静的リソースは、jar ファイルに組み込み、Spring Boot が提供する静的コンテンツ配信機能を利用して配信することにしました。
また、バックエンドには Gradle プロジェクト、フロントエンドには Node.js プロジェクトを使用し、Node.js プロジェクトを Gradle プロジェクトのサブプロジェクトとして扱っています。
これは、バックエンドとフロントエンドのビルドライフサイクルを統合することで、開発効率を向上させることが主な目的です。
設計手法
ドメイン駆動設計
バックエンドでは、ドメイン駆動設計を採用しました。
小規模なアプリケーションにおけるドメイン駆動設計の適用はオーバーエンジニアリングにつながる恐れがあります。
しかし、より良いコード品質や実装手法について学びつつ、それをアプリケーションの継続的な改善に活かしたいと考えたため、戦術的パターンを中心に取り入れることにしました。
ICONIX プロセス
モデリング手法には、 ユースケースを中心に据えたモデリング手法である ICONIX プロセスを導入しました。
今回は、振る舞いの明確化と詳細な要件定義を目的として、ドメインモデリング / ユースケースモデリング / ロバストネス分析のみを行っています。
また、ICONIX プロセスは UML を活用したモデリングと設計に焦点を当てているため、多くのドキュメントを作成することになりますが、保守コストを考慮して図の一部は省略またはカスタマイズして作成しました。
アーキテクチャ
アーキテクチャには、オニオンアーキテクチャを採用しています。
このアーキテクチャは、外側から内側への依存関係を持つレイヤー化された設計を提供し、各層を明確に分離することで柔軟でテスト容易なコードが構築でき、ドメイン駆動設計とも相性が良いため採用しました。
モデリング成果物
■ ドメインモデル図
■ 画面遷移図
■ E-R 図
※ ロバストネス図やエンドポイントに関する図についてはサイズが大きくなるため割愛しました。よろしければ、別途 GitHub をご参照ください。
テスト
テストスイート全体の保守コストを削減しつつ、正しく機能することを保証するために、各レイヤーごとに以下のようなテスト観点を設定しました。
レイヤー | テスト観点 |
---|---|
アプリケーション層 | 個々の要求が適切に実装されていることを確認するためのテスト |
プレゼンテーション層 | エンドポイントの動作とアクセス制御を検証するためのテスト |
インフラ層・ドメイン層 | 複雑な処理や重要な処理など、必要に応じたテスト |
アプリケーション層のテストでは、ロバストネス図上のコントローラに基づいて作成したテストケースを使用しています。
また、インフラ層とドメイン層のテストは、アプリケーション層のテストが広範囲をカバーしていることを考慮して、このようなテスト観点としました。
なお、バックエンドとフロントエンドを統合した E2E テストに関しては、UI の頻繁な変更を踏まえて手動テストを採用しました。
フロントエンド
- Vue.js 3.2.47
- Node.js 18.14.1
- npm 9.6.7
- Jest 29.5.0
- Webpack 5.82.0
- Babel 7.21.8
- Bulma 0.9.4
学習コストを抑えつつ、基礎を着実に築くために、バックエンドで動的に Web ページを生成し、Vue.js を利用して DOM 操作が必要な部分を構築するという構成にしました。
Vue.js に関しては、学習曲線が緩やかで、再利用可能な UI コンポーネントの作成や管理がしやすいという理由で採用しました。
また、動的型付け言語での開発経験がなかったため、学習を兼ねて TypeScript ではなく、JavaScript を使用しています。
インフラ
- AWS (ECR, ECS (Fargate), RDS (MySQL), S3, SES, CloudFront, ALB, Route53, Systems Manager, CloudWatch)
- Terraform
- CircleCI
- Docker / docker-compose
■ AWS
AWS ECS (Fargate) を採用した理由は、ローカル環境と本番環境で同様の条件でアプリケーションを実行できる点と、物理サーバーの管理を意識せずに運用できる点にあります。
Systems Managerに関しては、本番環境における環境変数を集中管理するために導入しました。ECS タスクの起動時にこれらの値を取得するように設定しています。
S3 は、ユーザーがアップロードした画像を保存するために使用し、加えて CloudFront を導入することで、独自ドメインを使用した HTTPS 通信による高速な画像配信を実現しました。
また、開発環境では、MinIO を使用して画像の保存や配信をシミュレートしました。
メールサーバーには SES を採用し、開発環境においては MailHog を導入してメール送受信の確認テストを行っています。
メールの利用シーンは、お問い合わせが行われた際の確認メールの送信です。
■ Terraform
Terraform は、AWS 環境を一貫して迅速に構築するために導入しました。
もともと「開発段階でも本番環境における動作確認を行いたい」と考えていたのですが、開発フェーズではリソースを常に稼働させる必要がないため、必要な時にのみ稼働させてコストを削減したいと思いました。
しかし、手動による環境構築はミスが発生しやすく、一貫性の維持も難しいため、Terraform を導入することでこの問題を解決することにしました。
また、同じく IaC ツールである AWS CloudFormation も検討しましたが、Terraform の方が構文が読みやすく、ドキュメントが充実していると感じたため、こちらを採用しました。
■ CircleCI
CI/CD パイプラインに関しては、CircleCI を採用しました。
採用した理由としては、GitHub とのシームレスな統合性が提供されており、AWS ECS へのデプロイも容易に自動化できるという点が挙げられます。
なお、CircleCI では develop ブランチへのプッシュ時に自動テストを実行し、main ブランチへのマージ時には、AWS 環境への自動デプロイを行う設定をしました。
反省点や今後の課題
■ バックエンド
バックエンドの開発においては、戦術的パターンを中心にドメイン駆動設計を取り入れました。
実践を通して、ビジネスロジックを反映したコードやテスト容易性の重要性など、多くのことを学ぶことができましたが、正確な解釈ができているのか、過剰な複雑性を生じさせていないかなど、不安が残っているというのが正直なところです。
根拠と自信を持ってより適切な実装ができるようになるべく、今後も引き続き学習を進めていきたいと思います。
■ フロントエンド
開発の初期段階ではバックエンドの開発に重点を置いており、フロントエンドについては認識が甘い部分がありました。
しかし、利用者からは「もう少し直感的な画面表示にしてほしい」「画像の表示速度はもう少し早い方が好ましい」といった、ユーザーインターフェースや操作性に関する意見が多く寄せられ、その重要性を痛感しました。
また、そのフィードバックを元にフロントエンドを改善する際、その変更がバックエンドに波及し、予想以上に修正範囲が拡大することがあり、もっと慎重にシステム全体の設計を検討するべきだったと反省しました。
■ 機能
機能面に関しても、まだまだ課題があると感じています。
例えば、共有機能におけるユーザー検索では、ユーザー名を利用していますが、現在の設計では重複を許容していないため、ユーザー名の選択肢が非常に制限されます。
しかし、このアプリケーションは家族などの閉じたコミュニティでの使用を想定しているため、お父さんやお母さんなどのコミュニティ内での役割を示すユーザー名が好まれる可能性が高いと予想されます。
この問題に対処するため、メールによる招待やメールアドレスを利用したユーザー検索などの解決策を検討していますが、プライバシーやセキュリティの観点を考慮すると判断が難しく、改善には至っていません。
また、ログや監視、パフォーマンス最適化などの非機能要件についても十分に対応できていないと感じているため、学習を重ね、改善に努めていきたいと考えています。
さいごに
長文となってしまいましたが、最後までお読みいただき、ありがとうございました。
Web アプリケーションの開発やこうした文章を書くことが初めてということもあり、拙い部分があったかもしれません。
情報の正確性を最優先に考え、要約や整理に努めましたが、不正確な点や読みづらい箇所がありましたら申し訳ありません。
ご意見やアドバイスなどがありましたら、ぜひコメントをいただければ幸いです。