ASP.NET Coreを使った独自フレームワーク開発の考察

ASP.NET CoreをベースとしたWebアプリ用フレームワークを開発するためのナレッジ置き場です。
ASP.NET Core3系のMVC + Razorを使用する前提の情報です。

まだ思い付きで書いているレベルです、ごめんなさい。

設計方針

  • MVC型のWebアプリケーションを想定。WebAPIとしての使用も想定。
  • 業務側での製造・テストまでの工期短縮や効率化を支援するため
  • クラウドとの統合
    クラウド環境での実行を想定した設計を行う。
    可用性と拡張性を担保するための構成を可能とする
    信頼できないネットワークでの実行を前提したエラーハンドリングを行う。
    分散トランザクションの性能性・完全性のトレードオフ
  • 作りすぎない
    フレームワーク単独での開発は利益を生み出さず、何らかの案件対応の中に組み込まれて実施される場合が多い。結果として、納期やコスト制限が厳しくなる。また、フレームワークは業務開発より先行するため、業務要件があいまいな局面で開発を進めないといけない状況が多い。
    このような状況で、使うかどうか分からないもの開発する余裕はないため、最小限の開発対象を見極める必要がある。
    業務に依存するような処理は業務側(業務共通とよばれる業務側でもスキルの高いメンバで構成されるチーム)してもらう。
    どのようなケースで使用するのかを明確にすることで、その機能の必要性を判断できる。ケースを言えない=使わないかもしれない可能性がある。例えば、アップロードする場合に使用する、等の一般的なケースを挙げられたとしても、業務でそのようなケースがあるのか不明である。可能であれば業務要件や業務フローと照らし合わせてケースを列挙することを推奨する。
  • 実装の流用
    開発対象の機能を独自に実装するとどうしてもコスト高や品質低下の懸念があります。可能であれば、サードパーティーライブラリの利用や別案件の実装等のように、性能や品質がある程度担保されたものを流用することを推奨します。
    サードパーティーのライブラリを採用する場合、追加費用・ライセンス条項・信頼性・セキュリティ等を考慮する必要があり、組織によっては決断の遅延や拒否される場合があります。このような場合は、独自の実装も必要かもしれません。
  • フレームワークを使う業務チームの学習や使用コストの低減
    最新すぎる技術の採用、過度な抽象化等のレベルの高いことをやりすぎると、使用する側の学習コストが高くなるので「ある程度」で妥協する必要があります。
    使用する側で複雑さを隠蔽するような設計、ガイドやサンプルの充実、トレーニングが望まれます。
  • 実行環境に応じて動作を切り替えられる
    構成ファイル等のスイッチにより、実行環境に応じた動作に切り替えられるようにする。
    開発環境では、テストやデバッグの容易性を考慮し、詳細なエラー情報を出せるようにする。WebAPI呼出し、FTP/SCP、メール送信、FAX送信、帳票生成等、外部システムと結合する機能では、開発環境に準備できない場合が多い。このような状況でも開発環境で動作確認やテストを可能とするために、スタブを用意する。
    (ここでいうスタブとは、本番とインターフェイスは同じだが内部動作が異なる処理を想定している。例えばFTP操作を行う機能では、開発環境ではFTP処理を行わず固定のファイルを返却するような処理を行う。)
  • 品質の担保
    業務機能の実装とは異なり、フレームワークが複数の案件で使用されるため影響範囲が広く、性能・品質・セキュリティに関する十分なチェックが必要である。また、業務側の実装のように一度作ったら終わりではなく、業務側からのフィードバックを受け、常に改善していく必要がある。度重なる改善での負荷を軽減するために、単体テストツールの使用を推奨する。(逆に、一度作ったら終わり、のような業務側での実装の場合、必ずしも単体テストツールは必要ないかもしれない。)

アーキテクチャ

フレームワークとして前提とするアーキテクチャを定義します。

フレームワークコア機能

フレームワークのコア機能に関する設計方針を定義します。

認証と認可

フレームワークで想定する認証・認可方式と実現方式を定義します。

セッション管理

セッション情報のストアやアクセス方法等の管理方式を定義します。

画面遷移制御

画面遷移の制御方法を定義します。
(フォワード、PRGパターン等)

例外処理

共通の例外ハンドリング方式を定義します。
また、フレームワークで用意する例外クラスと利用ケースについて定義します。

データベース操作

前提とする分離レベル、トランザクション制御方式等を定義します。

セキュリティ対策

一般的なWebアプリに対する攻撃の中で、フレームワークとして影響する部分の特定と対処方針を定義します。

拡張方式

業務側での拡張方法を定義します。

フレームワーク提供部品

フレームワーク側で提供する部品を定義します。

  • 状態を保持しない汎用的な処理はユーティリティ、状態を保持するものはサービスまたはクライアントとする。業務ロジックを実装するものはサービス、特定のサーバに対応する処理を実装する場合はクライアントとする。

認証部品

  • 認証部品: ASP.NET Coreでは提供されない認証ハンドラである。
  • アクセス制御部品: 画面アクセス時の権限有無をチェックするミドルウェアである。権限がない場合は認可エラー画面へ遷移する。
  • 権限判定サービス: 特定の権限の有無を判定するサービスである。画面上の項目やボタンの表示有無やサーバ側での権限チェックで使用する。権限の判定で使用するユーザ情報はセッションから自動的に取得する。

バリデーション部品

  • 基本はASP.NET Core標準の検証属性を使用する。
  • 日本固有の業務やロケールを扱うために追加を検討したい検証属性は次の通り。
    • 文字種: 半角の大小英字・数字・記号・カナ、全角の大小英字・数字・記号・カナ・ひらがな
    • 日本語範囲: サロゲートペア、第一水準、第二水準、環境依存文字等
    • 日付: システム日時や特定日時を基準にした過去・未来・範囲
    • 和暦範囲:和暦と有効年月日範囲
    • 年齢: システム日時や特定日時を基準にした年齢制限
  • 一般用途として追加を検討したい検証属性は次の通り。
    • 相関必須属性: ある項目が特定値になった場合は必須とする。
  • 実際に入力値を検証するためのロジックは、複数項目を使った検証をサーバ側のモデルで検証する等のように検証属性以外でも使用する可能性があるため、検証属性の内部に実装するのではなくユーティリティ化して広く使用できるようにすることを推奨する。
  • int, Decimal, DateTimeはnullを代入できないため、既定では暗黙的に[Required]が指定される。int?等のnull許容型に対する入力検証を無効にするためには、StartupでSuppressImplicitRequiredAttributeForNonNullableReferenceTypesを構成する必要がある。
  • Attributeを継承したクラスで入力検証の実現が可能。検証対象の要素をHTMLレンダリングする際の処理は、AttributeAdapterまたはIClientModelValidatorを使う方法がある。前者の場合、入力検証属性に対応するアダプタを提供するためのIValidationAttributeAdapterProviderを実装したクラスをDIする必要がある。
  • 参考: Model validation in ASP.NET Core MVC and Razor Pages
  • jQuery ValidateをベースとしたMS独自のjQuery Unobtrusive Validationというライブラリを使用する。このライブラリを有効にするためには、既定プロジェクトのwwwroot/libにあるjquery.validate.unobtrusive.min.jsと依存するjquery.validate.jsをページに組み込む必要がある。各画面共通で使用するなら_Layout.cshtmlで、必要に応じて使用するなら個別画面で宣言すれば良い。

ユーティリティ

  • 文字列操作: パディング
  • チェック: 各種文字種、サロゲートペア、第一水準、第二水準、チェックデジット(ISBN、マイナンバー等)
  • 日時演算: 和暦西暦変換、月初日取得、月末日取得、日時加減算
  • 書式変換: 日付/日時書式変換、カンマ・小数点編集、BASE64変換
  • 暗号化: ハッシュ値生成(SHA-2等)、安全な乱数生成(SecureRandom)、共通鍵暗号化/復号化(AES)、公開鍵暗号化/復号化(RSA)、パスワードハッシュ生成(PBKDF2/bcrypt等)
  • リフレクション
  • Dependency Injection
  • Razor: ページレンダリングや検証属性の実装を簡略化するためのロジック
  • Web: ファイルダウンロード、エンコード変換等
  • ファイル: ファイルシグネチャ
  • QR/バーコード:
  • CSV/固定長ファイルシリアライザ
  • アンチウイルスチェック
  • CAPTCHA

外部システム連携部品

  • 汎用インターフェイス
    • REST API
    • FTP/FTPS/SFTP/SCP
    • SMTP
  • 特化インターフェイス
    • サードパーティーのメール/SMS/FAX
    • HULFT
    • 決済ゲートウェイ

画面部品

  • ダブルクリック防止: submitボタンの重複送信を防止する。ajax系ボタンの場合は実行後にボタン押下可能な状態に戻す。
  • デフォルトキー制御: 画面上でエンターキー押下時に所定のCSSクラスがあるボタンを押下する。
  • フォーカス制御: 画面表示時のフォーカス位置、フォーカス移動時の順番等を制御する。

フレームワーク利用準備

フレームワークを使った開発を行うために提供するリソースは次の通り。

  • フレームワーク説明書
  • 利用ガイド: 設計時の注意点、製造・テスト方法を説明する。
  • サンプルプロジェクト(ミニアプリ): フレームワークを使って、ログイン・検索・登録・更新・削除等の基本的な操作を実装した最小限のWebアプリのサンプル。このサンプルを使用して、業務側での基本的な実装方法を説明する。単体テストコードもバンドルする。
  • サンプルプロジェクト(部品説明用): 部品一覧とそれぞれの利用サンプルページを持つアプリ。業務側で特定の部品の使い方や動作を理解するために使用する。(上記に部品説明を混ぜる煩雑になり理解が難しかったり、部品の動作確認が簡単でできなくなるの上記プロジェクトと分離する。)
  • ひな形プロジェクト: 実際に業務側でフレームワークを使ったアプリケーションを開発する際に使用するひな形プロジェクト。

業務側に提供するツール群は次の通りである。

  • 区分管理ツール: 区分定義から列挙体やDB登録用のINSERT/UPDATE文を生成する。
  • マスタ管理ツール: マスタ定義からDB登録用のINSERT/UPDATE文を生成する。
  • メッセージ管理ツール: メッセージ定義からプロジェクトにインポート可能な形式でのデータを生成する。
  • テーブル定義管理ツール: テーブル定義に基づいて、DB登録用のCREATE TABLE/INDEX文の生成や、エンティティクラスを生成する。
  • 権限管理ツール: 権限の定義に基づいてコードやDB登録用のINSERT/UPDATE文を生成する。複雑なパターンでは、ロール・メニュー・画面・機能の定義を紐づけた権限データの生成が必要になり、それぞれの定義との整合性を担保する必要もある。

業務側で使用する開発環境は次の通りである。

  • 開発環境構築手順
  • CI環境利用マニュアル
  • CI環境構築手順、管理手順

フレームワークの運用・保守

初期開発後の運用管理方法をルール化する。
期間や工数に余裕をもった改善計画を事前に立案しておくことで、余剰人員が発生した際にスムーズに作業を依頼できる。(経験的に、余剰人員が発生した際はフレームワークの改修に充てられることが多いので、そのような場合でもスムーズに作業を依頼できるようにしておく。)

  • 機能拡張やバグフィックス事項の管理と対応計画の決定
  • 基本的なバージョン管理方法
    機能拡張やバグフィックス、リリース方法等のルールの明確化
    コミットの承認者の選定
    一般への公開場所の選定と決定
  • 業務側からのフィードバック方法(利用後の感想や問題点・改善点等)
  • CI環境の準備と利用

参考

プロジェクトの構成

想定される業務共通部品

  • ログイン・ログアウト画面: シングルサインオンの場合は開発用のログイン画面等
  • ログイン後画面用の画面テンプレート(ヘッダ、フッタ)
  • メニュー表示部品
  • 共通コントローラ部品
    • ヘッダやフットで表示する情報の提供(ユーザ名や所属、画面名や説明等)
    • 各画面で使用頻度の高いセッションやユーザ情報等のオブジェクトの提供(コンテキストオブジェクトの提供)
  • 共通完了画面
  • 区分値名表示部品: 確認画面等でエンティティの情報を表示する際、一般的にエンティティには区分値しか格納されていないため、区分名を表示できない。区分値に対応する区分名を表示するためのヘルパー部品。
  • 非同期メール送信部品: 重要なメールの送信管理を行うための部品。登録されたメールの送信状況を管理し、必要に応じて再送も行う。多要素認証等のようにリアルタイム性や到達確実性が重要でないメールに関しては、メール送信部品を直接使用する想定。
  • 業務日付・カレンダー管理部品