プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

機能要件と非機能要件の判別

要件は機能要件と非機能要件に分類される。

ある要件が機能要件なのか非機能要件なのかどのように判別すればよいだろう?

ある要件を見たときそれが機能要件と非機能要件か判断できないことが初学者にはよくある。

現実の話をすると現場でそんなことで悩むことはない。非機能要件の議論をするときには会社やIPAが作成した非機能要件一覧を片手に行う。だから今話していることは非機能要件だとわかるのだ。そして何度か経験を積むうちに自然と両者を判別できるようになっていく。

しかしそんな現場の話は置いておいて純粋な知識や試験対策として判別できるようになりたい人もいるだろう。

そんな人たちの助けになるかわからないが自分が使っている判別方法を紹介する。


定義

まずは機能要件と非機能要件のよくある定義を紹介する。

機能要件

  • 機能要件はソフトウェアが「なに」をするか定義する。

非機能要件

  • 非機能要件はソフトウェアが「どのように」機能要件を実現するかを定義する。

初学者の悩み

「ユーザがログインに10回失敗したらアカウントを一時無効にしたい」という要件があったとする。

これは上記の定義に照らし合わせたとき、機能要件と非機能要件のどちらだろうか?

「既存の顧客データを新システムに移行したい」という要件があったとする。

これは機能要件と非機能要件のどちらだろうか?

初学者の多くが悩む。機能要件も非機能要件も見えてしまうのだ。

よく見かける機能要件と非機能要件の定義はわかりやすさを優先した結果、具体性を欠いていることが原因ではないかと考える。


私的判断基準

初学者の役に立つか自信はないが私が基準としているのは以下の2点だ。

  • システム化対象の業務フロー図に登場するものは機能要件
  • それ以外はすべて非機能要件

判断の例

システムを開発するということは自動化したい定常的な業務があるということだ。システム開発ではまずその業務を分析して業務フローを洗い出して業務フロー図を作成する。そしてその業務フローを自動化するのだ。システム化対象の業務フロー図に乗っているものは機能要件とみなすことができる。

業務フローという言葉がしっくりこない人は「作業手順」という言葉に置き換えてよいだろう。

例えば経費処理業務の業務フローは以下のような流れになるだろう。

申請者が申請書に必要事項を記入する
      ↓
上司は申請者の申請を承認する
      ↓
申請者は申請書を経費処理担当者に送る
      ↓
経費処理担当者は申請書の内容を確認する
      ↓
経費処理担当者は申請者の経費精算口座に振り込み処理をする

なんとなく「業務フロー」のイメージがついただろうか?

では、この中に「申請者がログインに10回失敗したらアカウントを一時無効にする」や「既存の従業員口座データを新システムに移行する」という要件が入りそうなところがあるだろうか?

ない。細かい説明をしなくとも言葉のレベル感が全然違うことがわかるだろう。

よってこれらの要件は機能要件ではなく非機能要件と判断できる。


定常的な業務か

前項で「定常的な業務」といったが「定常的」という点は重要なポイントだ。システム化したいのは定常的な業務である。

もし要件の中に1度きりの作業が現れたらそれはおそらくシステム化対象の業務ではない。それは導入時に必要とされるなんらかの管理系作業だろう。これらの作業自体は慎重な検討、準備が必要で、作業を補助するAdhocなツールが作成されることもあるだろうがそれはシステムの一部ではない。

例えば先程の例の「既存の従業員口座データを新システムに移行する」という要件は導入時に1度しか発生しない。Adhocなデータベース移行ツールが作成されるだろうが、それは経費処理システムの一部にはならない。この1度しかない作業が経費処理システムの業務フローに現れることはないので非機能要件と判断できる。

非機能要件を定義するためのツール

非機能要件が多すぎるシステムは得られる効果以上に開発に時間がかかり高コストになる。

非機能要件が少なすぎるシステムは品質や運用性が低下し、技術的負債を抱えた状態でリリースされることになる。技術的負債は積み重なることはあっても精算されることはなく、システムは破滅へと向かっていく。

設計の早い段階から適切な非機能要件を定義することの重要性は近年広く認識されるようになった。

しかし非機能要件の範囲は非常に膨大だ。機能要件でない要件はすべて非機能要件だからだ。そんな大きな話をどのようにまとめればよいのだろうか?

非機能要件をまとめるのに便利なツールが存在するのでここで紹介する。


RASIS

RASISとは、コンピュータシステムに関する評価指標の一つだ。「信頼性」「可用性」「保守性」「保全性」「安全性」の5つからなり、非機能要件の一部を構成する。

RASIS」は5つの要素の頭文字である。

  • Reliability:信頼性
  • Availability:可用性
  • Serviceability:保守性
  • Integrity:保全
  • Security:安全性

広く知られている指標だが正直言って、今日のシステムに求められる非機能要件を整理する用途には全くの役不足だ。この5つでは非機能要件を全く網羅できない。

そこで役に立つのが次に紹介する非機能要求グレードだ。


非機能要求グレード

https://www.ipa.go.jp/sec/softwareengineering/std/ent03-b.html

最も心強い味方がIPAが公開している「非機能要求グレード」だ。

開発者とユーザが非機能要件について議論しやすいようにと作成された非機能要件のメニューのようなものだ。

そのまま議論に利用できる優秀なテンプレートまで用意されている。

  • グレード表
    • 項目一覧の中から重要項目のみ抽出したもの
    • 「こういうシステムならこういう要件にするとよい」という例も記載されている
    • 非機能要件定義の初期段階に利用することを想定している
  • 項目一覧
    • 全非機能要件の一覧
    • グレード表の議論完了後、より広い議論をするために利用する想定
  • 活用シート
    • グレード表と項目一覧をマージしたExcelファイル
    • 実際の議論に使用する
  • 樹形図
    • 非機能要件の樹形図
    • この樹形図で今、全体のどこを議論しているのか参加者の認識を合わせながら活用シートで議論することを想定している

非機能要求グレードはシステム基盤に着目しており、アプリケーションに求められる非機能要件は取り扱ってない。アプリケーションに求められる非機能要件については別途、補う必要がある点に注意が必要だ。OWASPなどを参考にするのもよいだろうが、手っ取り早く進めたい場合は次に紹介する経産省の非機能要件定義書がおすすめだ。


SH29_999_非機能要件定義書

https://www.meti.go.jp/information_2/downloadfiles/kokuji20180320c8.pdf

アクセンチュア株式会社が作成した原案をもとに、経済産業省が作成した監督部電子申請システムの非機能要件定義書である。

アプリケーションに求められる非機能要件も含まれているため、非機能要求グレードを補完する形で利用するとよい。

こういう形で高品質なサンプルが公開されているのは有り難い限りだ。

SOAとMicroserviceアーキテクチャ

SOAとMicroserviceの違いについて調べると様々な答えが出てくる。

「ESBとSOAPWSDLを使うのがSOAだ」

「サービスの粒度が大きいがSOAで小さいのがMicroserviceだ」

「MicroserviceはSOAだ」

これは話し手の視点が異なることが原因だ。人によって「SOA」の意味が異なるからだ。

ここではSOAの歴史とMicroserviceアーキテクチャとの比較について述べる。


Service Oriented Architecture

90年代

SOAは特定の製品、規格、技術を前提としないアーキテクチャだ。

SOAの概念は90年代に提唱された。システムをサービスという単位に分割して開発し、各サービスが公開するAPIを組み合わせて1つのシステムとして動作させるシステム・アーキテクチャだ。

しかしこの頃、SOAはほとんど注目されなかった。

2000年代前半

SOAは2000年代に一転して脚光を浴び、様々なプロジェクトで採用されるようになった。その背景には開発済みの既存システムを相互接続して新しい高付加価値なシステムを開発したり異システムを統合したいという要望があった。

2000年代中盤〜後半

2000年代中盤〜後半になるとSOA向けに様々な技術、製品が開発される。

  • SOAPXMLベースのRPCプロトコル
  • WSDL:インタフェース定義言語
  • UDDI:サービスのAuto Discovery
  • BPEL:サービスの連携を記述する言語
  • BPMN:BPELを図として表現するための表記法
  • ESB:サービス間のバス

一方、SOAの実装技術が複雑化、肥大化していった反動でシンプルなREST APIが次第に注目を集めるようになっていったわけだがそれはまた別の機会に語るとしよう。


Microserviceアーキテクチャ

Microserviceアーキテクチャは小さなサービスを組み合わせてシステムを構築するアーキテクチャだ。

この説明だけだとSOAと区別がつかないだろう。Microserviceアーキテクチャの特徴を見てみよう。

Microserviceアーキテクチャは現代の大規模サービスに求められる性能、可用性、拡張性を実現することを目標としている。このため一つ一つのサービスが独立して開発、デプロイ、増減設可能な単位となっている点が特に重要視され、サービスは小さく、かつStatelessでDisposableに設計される。


各コメントの分析

「MicroserviceはSOAだ」

SOAの当初の概念を念頭に両者を比較するとMicroserviceアーキテクチャSOAの一種とみなすことができる。SOAはサービスを組み合わせてシステムを作ろう、という広く抽象的な概念で、MicroserviceアーキテクチャSOAの中でも性能、可用性、拡張を追い求めた具体例だと言える。

「MicroserviceはSOAだ」、という意見はこの観点から語られている。

「サービスの粒度が大きいのがSOAで小さいのがMicroserviceだ」

SOAの概念自体はサービスを独立して開発、デプロイ、増減設することを求めていない。よって一般的なSOAのサービスの単位はMicroserviceより大きくなる。StatelessnessやDisposabilityは考慮されない。

「サービスの粒度が大きいのがSOAで小さいのがMicroserviceだ」、といった意見はこの差に着目したコメントである。

「ESBとSOAPWSDLを使うのがSOAだ」

SOA」と言った場合、当初の概念ではなく2000年代中盤〜後半のSOAの実装例を指していることがある。個人的な印象ではこの視点で話している人の方が多数派だ。このとき「SOA」という言葉の中にはSOAPWSDL、ESBといった技術、製品群が含まれている

この視点から語ると「ESBとSOAPWSDLを使うのがSOAだ」となる。

API Gateway

API Gawewayという言葉をよく目にするようになった。

アーキテクチャ議論でよく出てくるのでなにを指しているのか理解しておきたい。

API Gatewayなしの場合

f:id:hogehoge666:20200824021830p:plain

  • クライアントは「発注」という処理のために複数のサービスをREST APIで呼び出す
  • 各サービスはTLS、認証/認可、Logging/分散Tracingなどを個別に実装している

API Gatewayありの場合

f:id:hogehoge666:20200824021843p:plain

  • クライアントは「発注」という処理のためにAPI Gatewayを一度呼び出す
  • API Gatewayは「発注」処理に必要な複数のサービスを呼び出してクライアントに応答を返す
  • サービスがREST API以外のAPIを公開していてもAPI Gatewayが吸収する
  • TLS、認証/認可、Logging/分散TracingなどCross Cutting ConcernはAPI Gatwayが実装している

メリット

  • クライアントの実装がシンプルになる
  • サービスの横断的関心事をAPI Gatewayに実装することでサービス開発者はビジネスロジックに集中できる
  • 既存のSOAPなどを使用するサービスを再利用できる
  • クライアントと各サービスが疎結合になる

デメリット


API Gatewayが提供する機能

  • Proxy機能
  • Mediator機能
  • API変換
  • 認証/認可
  • TLS
  • Request Rate Limiting
  • Retry/Circuit Breaker
  • Loadbalancing
  • Logging/Distributed Tracing(correlation)
  • Whitelisting IP address

Use Case

  • 既存のサービスと簡易なUIを組み合わせて新しい機能を提供する
  • MonolithicアプリケーションをAPI Gatewayの背後で隠蔽してマイクロサービスに分割する
  • Version混在、マイグレ、A/Bテスト、Canary Deployment
  • Microservice Architectural PatternにおけるAPI Gatewayパターン

API Gatewayパターン

  • マイクロサービスでAPI Gatewayを利用するパターンのこと。
  • 特定のマイクロサービス グループに対して単一のエントリ ポイントを提供する。

実装例


サービスメッシュとの違い

複数のサービス間の横断的な技術的課題を解決するソリューションとして他にサービスメッシュがある。

サービスメッシュとAPI Gatewayが解決する課題は一部重複しているが基本的には異なるものだ。

  • サービスメッシュはサービス間のEast/Westトラフィックを主に制御する。
  • API GatewayはNorth/South Trafficを制御する。
  • サービスメッシュはLayer3、Layer4の低次元な課題解決を行う。
  • API Gatewayは低次元な課題に加えてLayer7を含む高次元な課題解決ができる。

サービスメッシュについてはこちらの記事を参照して欲しい。

architecting.hateblo.jp

APIキーを管理するCloudサービス

Web APIにおけるキーの管理について以下の記事で述べた。

architecting.hateblo.jp

この中でAPIキーを管理するCloudサービスについて名前だけ紹介したが中身までは触れなかった。

ここでは著名なサービスをいくつか取り上げて簡単に説明する。どんなサービスがあるのか理解する一助になれば幸いだ。


1. AWS Key Management Service(KMS)

AWS KMSは簡単に言うと暗号鍵の安全な暗号化・復号化を提供するサービスだ。方式2においてAPIキーの暗号化に使用した暗号鍵(データキー)をさらにクラウド上の暗号鍵(マスターキー)で暗号化することで安全に管理できる。

APIキーを復号化する際はまずAWSからマスターキーで復号化した平文データキーを取得し、平文データキーでAPIキーを復号化する。

マスターキーはAWS上で厳重に管理されており、盗まれる可能性が低いためハッカーが平文データキーを入手できる可能性は低い

AWSAPIキーを管理する必要は残る点に注意が必要だ。


2. AWS Secrets Manager

APIキー自体をAWS上で管理するサービスだ。

APIキーはAWS KMSを利用して暗号化される。


3. Google Cloud Key Management Service

暗号化の鍵をクラウドで管理できる仕組みだ。

方式2においてAPIキーの暗号化に使用した暗号鍵自体をクラウドで管理し、復号化するときにはkeyの名前を指定して鍵を取得する。


4. Azure Key Vault

Azure Key VaultはAPIキー自体をAzure上で管理するサービスだ。

keyに対応したvalueを返すサービスで、APIキー以外にシークレット、証明書の3種類を管理できる。

Identity Basedで特定のVMやアプリケーションからのアクセスを許容できるため、コードから完全にCredential情報を排除できる。一方、そのVMやアプリケーションが乗っ取られたらすべてを見放題になってしまう。

ETagの利用例

主にHTTPのキャッシュなどで利用されるETagには複数のユースケースが存在する。

今回はETagのユースケースについて見ていこう。


ETag

ETagヘッダはRFC 7232(HTTP/1.1 Conditional Requests)で定義されているレスポンスヘッダである。

ETagの値はあるリソースの版数に対する識別子である。識別子が必ず一意になるものをStrong Validator、衝突する可能性があるものをWeak Validatorと呼び要件によって使い分けることができる。


1. キャッシュの有効性確認

最も一般的なETagのユースケースはキャッシュの有効性確認である。クライアントはETagを利用して条件付きリクエストを行い、キャッシュが有効な場合は304 Not Modified、新しい版数のリソースが存在する場合は200 OKをサーバから受け取る。

キャッシュやStrong Validator、Weak Valiatorの詳細については下記の記事を参照して欲しい。

architecting.hateblo.jp


2. 楽観的ロックアルゴリズム

ユーザAとユーザBが同時に同じ記事を編集し、最初にAが、次にBが更新した記事をPUTしたとする。このときAの変更はBによって削除されてしまう。これをロストアップデートと呼ばれたりする。ETagを利用して簡易的なロックを実装しろストアップデートを防ぐことができる。

編集開始時にユーザAとユーザBに編集前の記事のETagを渡す。AとBはPUTするときIF-MatchヘッダにETagを記載する。AのPUTは成功し、記事には新しいETagが割り当てられる。一方、BのPUTはETagが一致しないため失敗(412 Precodition Failed)する。


3. 中止したダウンロード処理の再開

中止したダウンロード処理を再開したいとき、クライアントはRangesヘッダにこれまでに受信したバイト数、IF-MatchヘッダにETagを記載する。サーバはETagが一致していた場合、残りのデータをクライアントに送信する。このときETagが一致しているのでダウンロード処理が中断されている間にデータが変更されていないことが保証されている。もしデータが変更されていた場合にはETagが一致しないため失敗(412 Precodition Failed)する。

DIとDIPとIoC

この3つの用語はなんとなく名前が被っている上に同じ文脈で登場することが多いため、初学者は混乱しやすい。

なにが違うのか、Dependency Injectionパターンを中心に関係性を整理してみよう。

f:id:hogehoge666:20200821205250p:plain


Dependency Injectionパターン

DIパターンについてはこちらの記事で詳細に説明しているので参照いただきたい。

architecting.hateblo.jp


Inversion of Control

DIパターンはもともとIoCの実現方式の1つとして登場した。

IoCとは制御が逆転するシステムアーキテクチャ設計である。

古典的制御

例えばUIから文字列を受け取って処理するソフトウェアを考えてみる。これを古典的なCLIプログラムとして実装すると以下のようになる。

1. プログラムは入力を促すCLIメッセージを出力する
2. プログラムは標準入力へ入力が来るのを待つ
3. プログラムは標準入力の内容を処理する

制御の起点はプログラムであり、プログラムから一時的に標準入力へ制御が移り、その後またプログラムに戻っている。

現代的制御

一方、現代的なGUIアプリケーションでは以下のようになる。

1. フレームワークがGUIが表示する。
2. フレームワークはGUIに入力が来るのを待つ。
3. GUIに入力が来るとフレームワークはプログラムを呼び出す。
4. プログラムは処理を実施する。

制御の起点はフレームワークであり、一時的にプログラムに制御が移り、その後またフレームワークに戻っている。制御の流れが古典的なCLIプログラムと逆転している。これがIoCである。

GUIに限らずフレームワークを利用した開発ではIoCは頻出するアーキテクチャだ。フレームワークを利用した開発が主流となった現在ではIoCは当たり前のこととなっている。

IoCの新たな適用領域

再利用可能なモジュールを組み合わせてソフトウェアを構築することが一般化していく中で、IoCの新たな側面が脚光を浴びるようになってきた。

再利用可能なモジュール同士は開発時点で互いのことを全く知らない。その互いのことを全く知らないクラス同士をどのように組み合わせるべきかという課題に対してもIoCの実現方式が有効だったのだ。

IoCの複数ある実現方式の中で特にGood Practiceだと思われたのがDIパターンだ。当初、DIパターンという名前がなかったためIoCと呼ばれていたがこの新しい用途は制御の逆転が主目的ではないことや他のIoC実現方式と見分けがつかないことから混乱が生じた。そこでMartin Fowler氏が整理してDependency Injectionパターンと命名した。


Dependency Inversion Principle

DIPはSOLID原則の1つでモジュール間の依存関係を適正な姿にコントロールすることを要求する設計原則だ。上位クラスで下位の具象クラスに依存してはならないと定めている。SOLID原則についてはこちらの記事を参照いただきたい。

architecting.hateblo.jp

現実には開発の中で上位クラスが下位クラスに依存するような状況は発生してしまうことがある。DIパターンはクラス間の依存関係を逆転させる効果があるのでこのような状況で依存関係を最適化するために利用できる。DIパターンはDIPの実現に非常に有用なツールと言える。

両方とも頭文字を取るとDIやDIPなので初学者は「言葉がちょっと違うだけで同じものなんじゃないか?」と勘違いしそうになる。違いをしっかり理解しておきたい。


まとめ

  • Inversion Of Control

  • Dependency Injectionパターン

    • IoCの実現方式の1つ。
  • Dependency Inversion Principle

    • モジュール間の依存関係を適正な姿にコントロールすることを要求する設計原則。
    • 依存関係を逆転させたいときにDependency Injectionパターンが利用できる。