【Blazor】電子サインができるWebサービスを4ヶ月かけて作った話
「自分で何か Web サービスでも作ってみたいなぁ」
そう思い立ったのが 2022 年 8 月中旬で、そこから 4 ヶ月ほどかけて電子サインができるサービス『SoySign』の β 版を公開しました。
どのくらいで作ったのか
正確にはわかりませんが、平均すると 1 日 1 時間くらいを 4 ヶ月続けていたので、おおよそ 120 時間くらいで作ったと思います。
なぜ作ったのか
このサービスを作った理由は、大きく3つあります。
1.需要が伸びていそうだから
今年の 8 月頃に以下のニュースを読みました。
ざっくり要約すると以下の内容です。
- 電子契約、電子署名の利用は 1 年で倍増している
- 利用したいと考えている人も増えている
- 利用した人が非常に便利だと考えている
せっかくサービスを作るのであれば、自己満足で終わらずに需要のあるものを作りたいと考えていたので、このあたりのサービスを作ろうと決めました。
2.価格帯が高めのサービスが多かったから
サービスを作る上で気になるのは、「競合」ですよね。
いろいろと調べてみると、月5,000〜10,000円くらいの料金がかかるサービスが多かったです。
思いついた当時は、「自分で作ってしまえば、もっと安く提供できるのでは?」と根拠のない自信がありました。
3.Blazor で実践的なものを作りたかったから
Blazor は以前から勉強をしていて、個人でブログや本を書いていたりもします。
しかし、これまでは localhost で試していただけだったので、実際に Web 上で動くサービスを作ってみたいという気持ちがありました。
Blazor を初めて聞いた人は、前に導入記事を書いているので、こちらを参照してください。
誰に向けたサービスか
いまのところは以下の人を想定しています。
- 中小規模の事業者向け
- 世の中の電子契約サービスを高いと感じている
- シンプルなサイン機能があれば十分と考えている
サービスの構成
ここからは、どのような構成でサービスを作ったのか、それぞれのサービスの使い方を紹介していきます。
全体構成図
全体の構成はこんな感じになりました。すべて Azure のサービスを使用していて、ベースは Blazor Server です。
App Service
App Service は、Web アプリをホストできるサービスです。
無料枠もありますが、独自ドメインを使う場合は有料プラン(B1 以上)にする必要があります。
画像は Linux OS なので比較的安いですが、Windows OS にすると倍くらい値段がするので、今回は Linux OS を選択しました。
ちなみに、Mac だと デプロイ先の OS が Windows でなければ Visual Studio からデプロイできない問題があったりするので、細かいところで Linux OS のデメリットを感じる場面があります。
余計なところで時間をかけたくない人は、Windows PC で開発するか、App Service の OS を Windows にするといいでしょう。
Azure AD B2C
Azure Active Directory B2C とは、サービスを運営する管理者が消費者を管理するために使う Azure 上の Active Directory を指します。
認証機能は自前で実装すると大変なのと、できるだけ Azure で統一したかったので、今回はこちらを使用しました。
実際に統合する手順について知りたい人は、ブログで紹介しているのであわせてご覧ください。
Microsoft Graph
Microsoft Graph とは、Microsoft のクラウドサービスにアクセスするための REST API とクライアントライブラリをいいます。
今回は Azure 上でユーザー管理をしていますが、直接データベースを参照することはできず、Microsoft Graph を経由してユーザーデータにアクセスする必要があります。
Azure SQL Database
Azure SQL Database は、ざっくりクラウド上で SQL Server が使えるサービスです。
一番安いプランだと月額 600 円ほどで使えるので、とりあえず動けばいいという場合は Basic でインスタンスを作るといいでしょう。
あとで自由にスペックを変更できるのも良い点です。
もちろん、ローカルで開発するときには SQL Database を使う必要はありません。
Windows の場合は PC に直接 SQL Server をインストールできますし、Mac の場合は Docker を使うことでローカルで開発ができます。
Mac の場合は Intel チップか M1 チップかで構築方法が異なるので、気になる人は以下の記事を参照してください。
App Configuration
App Configuration は Azure 上で機密情報を保存しておくことができるサービスで、シークレットキーなどをセキュアに管理することができます。
無料枠ではひとつの App Configuration しか作成できませんが、ラベルをつけることによって無料枠内で環境ごとに値を分けることもできます。
App Configuration の具体的な導入手順については、以下の記事を参照してください。
Azure Functions
Azure Functions はサーバーレスに処理を実行できるサービスのことで、関数をデプロイすることで外部から呼び出しができるようになります。
今回はメール送信の仕組みを Queue トリガーとして実装しました。
Queue トリガーは実行した直後にレスポンスを求めるものではなく、多少時間がかかっても実行されれば OK とする処理に向いています。
Queue トリガーの実行方法については、以下の記事を参照してください。
Application Insights
サーバー内でエラーが発生した場合、何かの方法で確認する手段が必要です。
Application Insights を App Service と連携することで、アプリ内で発生したエラーをいい感じに見ることができるようになります。
例外の一覧を見ることができ、そこからスタックトレースも確認できるので、App Service と合わせた使用は必須と言えるでしょう。
Azure Storage Container
Azure Storage Container は、ファイルをアップロードできる器のことです。
今回は、ユーザーからアップロードされる PDF ファイルを格納しています。
コンテナーの中には Azure Blob Storage があり、実際には Blob の方にファイルがアップロードされます。
Azure Blob Storage にファイルをアップロードする手順については、以下の記事を参照してください。
自分なりに工夫した点
全体構成図の話はこのくらいにして、ここからは自分なりに工夫した点をいくつか紹介していきます。
csproj をできるだけ分けた
個人開発であれば、「まぁソースコード見るのは自分だけだし、多少汚くてもいいか」と妥協してしまいがちだと思います。
動作確認くらいであればいいのですが、実際に公開するものとなるとある程度は整えておきたいと考え、プロジェクトを役割ごとに分けてみました。
プロジェクト | 概要 |
---|---|
SoySign.Server | スタートアッププロジェクト。画面まわりの処理を実装。 |
SoySign.Azure.Ad | Azure AD まわりの処理を実装。 |
SoySign.Azure.Storage | Blob ストレージへのアップロードや Queue トリガーのリクエストまわりの処理を実装。 |
SoySign.Rdb | SQL Database への接続まわりの処理を実装。 |
SoySign.ReliableTasks | Azure Functions の処理を実装。 |
SoySign.Shared | 共通で使えそうな処理を実装。 |
ステージング環境を用意した
個人開発であれば本番環境をひとつだけ用意していることも多いかと思いますが、今回は本番環境と同じことが確認できるよう、ステージング環境も用意しました。
サービス公開後はステージング環境で動作確認をして問題ないと判断したあとに、本番環境へリリースしたいと考えたためです。
サインアップ時に利用規約への同意を必須にした
今回、認証システムとして Azure AD B2C を使用していますが、デフォルトでは利用規約の確認必須機能はありません。
そこで、認証の流れをカスタマイズできる「カスタムポリシー」という機能を使って、実現しました。
合わせて、左下の方に利用規約とプライバシーポリシーのリンクも載せています。
もし同じようにやりたい場合は以下の公式ドキュメントが参考になりますが、カスタムポリシーを初めて触る人はかなりの時間を溶かしてしまう場合があるので、注意してください(私です)。
ログアウト後はトップページにリダイレクトした
Blazor でログアウトしたあとは、「Sign Out Success!!」みたいなブレーンテキストが表示されるページに飛ばされるのがデフォルトなのですが、それなりにダサいです。
個人開発であれば、できるだけ個人開発感を出したくないですよね。
パッと調べた感じではログアウトページのカスタマイズ方法も見つからず、どうしようか悩んでいたところ、「トップページにリダイレクトする」という方法を見つけました。
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Events.OnSignedOutCallbackRedirect = async context =>
{
context.HttpContext.Response.Redirect(context.Options.SignedOutRedirectUri);
context.HandleResponse();
await Task.CompletedTask;
};
});
こうすることで、再度ログインページが表示されるという挙動になり、幸せな気持ちになります。
作ってみての感想
localhost で開発をしていたときと比べて、ハマりどころがたくさんあり、とても苦労しました。
仕事でサービス全体をひとりで構築することはなかなかないので、それを経験できたのは大きかったかなと思います。
また、個人開発で技術的なところで詰まった場合は自分で解決しなければならないので、問題解決能力もグッと上がったような気もします。
一方で、サービスに対する手応えはどうかというと、「うーん」というのが正直なところです。
あたり前の話ですが、どうしても企業が作ったものと比較すると見劣りするところが多く、どこで差別化をしようかと考えています。
もちろん「価格」は差別化のひとつにはなる予定ですが、価格を理由に使っているユーザーはより安いサービスが出ると、そちらに移ってしまいます。
何か価格以外で差別化できる要素を見つけて、多くのユーザーに使ってもらえるシステムを目指していきたいです。
最後に
ここまで読んでくださりありがとうございました。
もしよければ、サービスを使っていただけると嬉しいです(現在 β 版です)。
Discussion