私は医療情報システムの開発を10年以上関わっていて、これまで様々なオプションソフトの新規開発を主としてやってきました。
医療情報システムにおいては特に、長期運用されて機能追加・変更され続けることから、持続可能なソフトウェア開発は必要だと感じています。
システムの新規開発に関わることになった、そんな過去の出来事を振り返ると、改めて持続可能なソフトウェア開発が必要だろうと思います。そのためにはソフトウェア設計と、開発メンバーのマインドが重要だと改めて思ったので、ここに書いてみようと思います。
きっかけ
既存システムを置き換える、新システム開発プロジェクトがスタートしたことをきっかけに、既存システムと轍を踏まないためにメンバーと試行錯誤していきます。
既存システム
関わっていた既存の医療情報システムは、10年以上機能追加され続けていて、ユーザー目線では超高機能なアプリケーションになりましたが、エンジニアから見ると変更しやすいソースコードではありません。
5年以上、多数のメンバーによって変更され続けているシステムは、機能追加・変更に時間がかかり、バグが混入しやすくなり、コードを読んでもよく分からないものになりがちです。
既存システムは、LANで接続した複数端末で動作するデスクトップアプリケーションで、昔ながらの設計というか、オブジェクト指向言語(C#)を使いながらも、中身は手続き型の思想でコーディングされています。
規模は何百万ステップになり、リファクタリングも無しに機能追加し続けた結果、特定のメンバーでなければ変更が難しい処理が多い、というエンジニアリング上の問題を抱えていました。
あるとき、新設計のクラウド型アプリケーションを開発する、というプロジェクトの初期メンバーとして関わることになりました。
デスクトップアプリからクラウドへ
既存システムはデスクトップアプリケーションですが、新規開発ではクラウドとデスクトップアプリケーションを組み合わせたもの、という方針でスタートしました。
医療情報システムには、デバイスとの連携や印刷の制御という要件もあり、Webアプリケーションではなく、クライアントのネイティブアプリケーションも開発することになりました。
最初のエンジニアメンバーは自分を含めて4人。基準となる既存システムはあるものの、何も無いところから新たに設計して作るという状況です。
APIサーバはASP.NETとEntity Framework、クライアントはWPFで作ることになっていましたが、これらを使って業務アプリケーションを開発した経験はありませんでした。
また、APIサーバはクラウドで動かすという方針がありましたが、クラウド開発の具体的な方法が分からないメンバーばかりでしたので、まずはローカルでサーバアプリケーションとクライアントアプリケーションを作ることになります。
プロトタイプとしてクライアントサーバのアプリケーション作成し、新しい技術の獲得と、既存システムの機能分析を並行して行いました。
そして、先々開発規模が大きくなることを前提に、規約や設計も考えなければなりませんでした。
初期メンバー
プロジェクト初期のエンジニアメンバーは次の通りです。PHPでWebアプリケーション開発していた2名(うち1人はDBエンジニア)、自分を含めて主にC#でデスクトップアプリケーションを開発していた2人というチーム構成です。
Webの経験はあってもC#を使ったことが無いメンバー。C#を使ったことがあってもWeb開発したことが無いメンバー。そして、全員APS.NET、WPFを業務アプリケーションに使ったことがありません。
一方、良かったと思うことは、全てのメンバーが自ら学習して新しいことにチャレンジしていくことができたことです。
分からないことや先が見えないことが多く大変でしたが、ネットや書籍の知識を持ち寄り、お互いが補完し合いながら前に進んでいくことができたのは良い経験でした。
規約と設計とマインド
動くものを作る必要がある一方で、既存システムを置き換えるとなれば、将来的に機能追加や変更が繰り返され、大規模なものになることが予想されます。
メンバーが増えれば、ソースコードが荒れないように、コーディング規約が必要となりそうなこと。設計思想を新しくしないと将来性が無いこと。これらを考えていくことになります。
規約は守られない
既存システムにはコーディング規約がドキュメントとして存在しています。
C#ではアプリケーションをEXE(実行ファイル)とDLL(Dynamic Link Library)で構成します。そして名前空間の概念もあるため、それらをシステムの業務単位で分けるための命名規則、メソッドの定義の仕方、細部に至っては三項演算子の使用禁止、といったものまで規約に書かれています。
既存システムの開発初期は、コーディング規約通りに作られていたようですが、時間がたつにつれ、メンバーが増えるにつれ、C#の言語バージョンが上がるにつれ、陳腐化したコーディング規約では、方針レベルの規約しか守られない状態になりました。
規約が数が多いと覚えきれない、規約を守らなくても動作に影響しない、言語バージョンが上がればその言語仕様の規約は無いので、当然のことなのかもしれません。
C#を使った開発では、Microsoft Visual StudioというIDEを使っていたため、新プロジェクトでは機能を活用してドキュメントに頼らない規約の導入を行いました。
当時、Microsoftが内部で使っていたツール(Style Cop)が公開されていました。これを利用することで、規約に従わないコードをビルド警告やビルドエラーにでき、コードの書き方を強制する方法をとりました。
当初は警告がうるさいですが、慣れてくれば警告が出ないコードが書けるようになるものです。
その後、Style CopがVisual Studioの標準機能として組み込まれ、自動でコード修正することができるようになることで、苦労せずに統一したスタイルのコードが書けるようになりました。
最終的には、プロパティや関数の説明、パラメータの説明が、もれなく記述されたソースコードになっていきました。
若干、変数名をそのまま説明にコピペして、説明になっていないなどの問題もありましたが。
設計どうするか
先が見えないながらも、大規模化していくこと、多数の開発者が関わること、機能追加や変更が続いていくことは確実でした。
C#はオブジェクト指向言語であり、変更に強い設計をしたいと思っていたものの、具体的にどのように作っていくのか悩みました。
開発を進めていくと、徐々にユニットテストができない問題に直面します。
プログラムは最終的に何らかの通信、またはDBにアクセスすることになるため、業務ロジックをテストしようとしてもできなくなりました。
色々と模索していく中で、新システム開発の基本となったのはSOLID原則でした。
SOLID原則は以下の5つです。(他に詳しいサイトや書籍があるのでここでは説明は割愛します。)
- Single responsibility priciple(単一責務の原則)
- Open Close principle(解放閉鎖の原則)
- Liskov substitution principle(リスコフの置換原則)
- Interface segregation principle(インターフェース分離の原則)
- Dependency inversion principle(依存性反転の原則)
特に、インターフェース分離の原則と、依存性反転の原則を取り入れることで、ユニットテストでは通信やDBアクセスをモックにして、業務ロジックをテストできるようになりました。
その他、サーバにCQRSを導入したり、ドメイン駆動設計を意識したりと、既存システムの設計と全く異なるものに変えました。
その後、既存システム開発をしていた多数の人員が、新システム開発に参画するようになります。
全ての人がSOLID原則を考慮できるとは限らないため、クラスと対になるインターフェースを必ず作成することや、クラス継承は使わない(使いたい場合は相談する)、というルールを設けたりもしました。
説明用の資料を作成するのに苦労したり、常々SOLID原則を説いて回ったりしていた気がします。
メンバーのマインドは重要
人員が増えた際も、最低限SOLID原則を守ってもらいたいと思っていました。
資料を作成して説明したり、コードレビューでSOLID原則を踏まえた指摘をする中で、見えてくるものがあります。
それは、今までの慣れた方法でコーディングしてしまうことがある、ということです。
当然、スケジュールに追われ始めると、従来の慣れたやり方が最も早く実装できるため、レビューする側も仕方ないこととして、一時的に目を瞑るということもありました。
結果的に起きたことといえば、SOLID原則の単一責務の無視と、解放閉鎖の破壊でした。
すると、事前に設計して用意していた再利用可能な機能が、徐々に特定の機能に特化してしまうのです。
もちろん、SOLID原則のことを聞いて学習し、実践しようとしてくれるメンバーもいますが、良い設計を浸透させるためには、自分自身も含めて、メンバーのマインドは重要だということを痛感しました。
Hackfestと設計
当時、開発とは別のクラウドチームが、クラウドの設計を行っていました。
一方、開発チームは、アプリケーションをクラウドに上げることなく開発を続け、クラウドで動かすにはどうすれば良いか、という状況です。
認証も後回しになっている、DBでマルチテナントを考慮していない、という状況でもありました。
そんな中、Hackfestに出会います。
Hackfest
Azureの技術サポート企業の支援を受け、Microsoft MVP 3名にサポートいただく手厚い環境で、3日間のHackfestを実施いただきました。
会議室に集まり、3日間で設計から実際に動くものを作り、課題を解決するというイベントです。
その場で手を動かして実装できるエンジニアが代表して参加しました。
課題はクラウド設計と、認証、マルチテナントを実現してクラウドで動かすこと。
認証についてはAzure AD B2Cを使う設計が提案され、サンプルプログラムをその場で業務アプリケーションに組み込みました。
DB全体の設計変更は時間がかかるため別の機会で対応するものの、一部のテーブルに限定してEntity FrameworkでRow Level Securityを実現。
クラウドチームが当初作成していた設計は、大幅に変更されました。
3日間でサーバアプリケーションはAzure App Serviceで動き、クライアントアプリケーションはAzure AD B2Cで認証でき、アクセストークンを用いて認証認可を実現したAPI呼び出しまで実現することができました。
設計による変更容易性
最終的には、Hackfestで目標としていた、サーバアプリをクラウドにデプロイして、クライアントアプリケーションとの通信を認証も含めて実現する、という全ての内容を3日間で完遂です。
新システムでは良いソフトウェア設計を目指し、クライアントアプリケーションのビジネスロジックがフレームワークに直接依存しないように、通信部分をリポジトリパターンで隠蔽するなど、その他諸々の設計が奏功し、ソースコードに対する変更は効率的でした。
認証機能と、アクセストークン付きでAPIを呼び出す機能については、それぞれ1カ所に変更を入れるだけで、全てのAPI呼び出しに対応することができ、設計による変更容易性を身をもって実感しています。
Hackfest最終日の最後にはラップアップの時間がとられ、参加者も感想を述べる機会がありました。
我々のソースコードに対して「こんなきれいなソースコードは見たことが無い」というコメントは印象深く残っています。
また、長いことクラウドで動かせず困っていたことが、わずか3日間で解決できたことに感謝を伝えると「元の設計が素晴らしいからです」という返事をいただき、設計に苦労した分だけ感涙にむせぶメンバーもいました。
最後に
やはり設計とメンバーのマインドが重要だと思う一連の出来事でした。
色々と設計を頑張ってきたメンバーにとっては、社外からの評価が良かったのは救いだったと思います。
ソフトウェア設計が重要
SOLID原則をはじめ、ドメイン駆動設計といったものを、実際の業務アプリケーションに取り入れるのはとても難しいと思うと同時に、設計は重要だと実感できるタイミングがあったのはとても良かったです。
世の中には色々と設計手法やデザインパターンが紹介されていますが、抽象的だったりサンプルコードも小さいなど、実際の大規模アプリケーションを開発する際に、どのように適用してどのような効果があるのかという、実践的な内容にはなかなかお目にかかれませんでした。
1度だけ動くプログラムを書くことはあまり難しくありません。
その後の機能追加や、変更しやすい状態を保ち続けていくのはとても難しいです。
そこで改めて思うことは、ソースコードに対して処理を追加するという考え方はやめる、処理では無く責務を追加するという考えを持つことです。
SOLID原則をはじめとして、良い設計とされるものを実践して行くことが、持続可能なソフトウェア開発にとって重要だということです。
設計を実践するきっかけになった書籍
メンバーのマインドが重要
メンバー自身が設計を実践するマインドも必要です。
特にSOLID原則のことを考えてくれるメンバーがもっと増えると良いと思います。
言われたこと、ルールだからやるというマインドでは、結局のところ設計もできないし、逆に壊されていくということを実感しました。
ソフトウェアの設計は応用です。1つの正解があるわけでは無く、設計手法を学んで考えながら、実践して行くことが重要だと思います。
完璧な設計はできないとしても、より良い設計を意識していれば、外れることはないと思いますし、良い方法を見つけられるのではないでしょうか。
このようなマインドを持ったメンバーが集まれば、持続可能なソフトウェア開発ができるようになるのかもしれません。
お知らせ
現在私が身を置くPHC株式会社 BXセンターでは仲間を募集しています。
多様なバックグラウンドの方が集まり、とてもチャレンジングな組織です。
少しでも興味を持たれましたら、以下のリンクをクリックしてくださいませ。