スタートアップの生命線は、スピードとPMF
メディカルフォースという会社は、名前に「メディカル」と入っているものの、実際には医療系のSaaSだけでなく、さまざまなサービスを展開している企業である。当初は、社名と同じ「medicalforce」というSaaSを2021年にローンチした。これは、保険診療のみならず、自由診療(保険が適用されない医療)を提供しているクリニックを中心に患者の予約から会計までを一括で管理できるサービスだ。そして2024年5月には警備業界向けの同様のSaaSを提供開始しており、バーティカルSaaSを多岐にわたって開発していくことを目指している会社である。そのため、ビジョンを「これからの産業の成長プロセスを合理化する」とし、より広範な事業展開を目指している。
CTOの畠中氏は、学生時代からインフラの構築やWebアプリの立ち上げに多数取り組んできた。2020年11月に株式会社メディカルフォースを設立し、現在のサービスをフルスクラッチで開発した。開発と並行して、深層生成学習を用いた研究が国際学会に採択されるなど、機械学習(AI)や最先端技術にも精通している人物だ。
畠中氏は「今回の話は、アーキテクチャ選定の難しさを感じている方や、組織が大きくなるにつれてスピード感が落ちてきたと感じる方に向けて、何かヒントを提供できればと思います。また、ドメイン駆動設計(DDD)についても触れる予定で、その本当の価値を伝えられればと考えています」と今回の講演のメインテーマに触れた。
スタートアップにとって、スピードが何よりも重要である。畠中氏は「このスピード感は創業期だけでなく、現在でも非常に重要。最初のプロダクトを開発し、それが世の中で使われるようになるまでには、PMF(プロダクトマーケットフィット)を達成する必要があります。PMFとは、既存のシステムから新しいシステムへとスムーズに乗り換えが行われる状態のことを指すと考えています」と語る。既存のシステムはExcelや競合の製品であるかもしれないが、新しいシステムが業務をより簡単にすることで、顧客が自然に乗り換えるような状況がPMFの達成となる。
新しいシステムを作る際には、元のシステムと比べて何ができるようになるか、どれだけ業務が簡単になるかを示すことが重要だ。しかし、元のシステムでできていたことができなくなる可能性もあるため、できなくなる部分を最小限に抑え、できることを増やさなければ、顧客は乗り換えてくれない。畠中氏は「自由診療の業界では、20年もの間使われていたシステムがあったため、PMFの達成に苦労した経験があります」と話した。
メディカルフォースでは、創業からの初期段階は、とにかく早く開発を進めることでさまざまな課題を乗り越えてきた。しかし、時間が経つと競合が増え、機能がコモディティ化してしまった。競合と真に差別化するためには、顧客と向き合い、独自のロードマップを実装するスピード感と能力が重要となる。畠中氏は、これを長期間維持することが、CTOとしてメディカルフォースの価値を支える役割であるとした。
ロジック層の複雑化がもたらす問題と解決への模索
PMFを達成するためには多くのことを迅速に実行する必要がある。スタートアップでは時間をかけていられないため、スピード感が非常に重要である。プロダクトが一定のシェアを獲得した後も、その優位性を保ち続けるためにスピード感が不可欠なのだ。
ただし、創業当初からのスピード感と、その後のスピード感は大きく異なると感じた。それは同社がDDDを導入するきっかけにもなった。「プロダクトとしての優位性を保つためには仕様変更や新機能の追加を迅速に行うことが求められる。重要なのはスピード感の維持であると感じています」(畠中氏)
仕様変更と改善のスピードを高めるには、コードの変更が容易であることが欠かせない。どこを変更するべきかが明確であり、変更によって何が起こるかが予測できる状態であれば、スピードを保つことができるだろう。さらに詳しく見ると、仕様変更においては、各関数やクラスの責務が明確で、見通しの良いコードであることが重要だ。変更の影響範囲が明確であるためには、依存関係がはっきりしており、依存の向きや範囲が限定的であることが求められる。
「結局のところ、よく言われる『高凝集・低結合』が非常に大切であるという結論に至りました。ただし、それを通常のアーキテクチャで実現するのは難しいと感じています。特に、私たちが取り組んでいるような大規模なバーティカルSaaSを構築する際、従来の3層アーキテクチャ(プレゼンテーション層、ロジック層、データアクセス層)では、コードが複雑になりがちで、これをどのようにきれいに保つかが今後の課題と感じています」
例えば、2つのロジック、AとBがあり、ロジックAでロジックBの一部を使用したい場合、それをロジックAに直接組み込むと、本来ロジックAが行うべきではない処理が増え、凝集度が低下する。また、ロジックAからロジックBを参照すると結合度が高くなり、高凝集・低結合から遠ざかってしまう。
畠中氏は、具体例として、薬品の在庫アラートを実装したケースを紹介した。例えば、在庫が10個以下になったらアラートを通知する機能を追加しようとした際、在庫の仕入れや在庫使用のタイミングで在庫の減算ロジックを呼び出す必要があった。しかし、在庫アラートのロジックを在庫管理のロジックに組み込むと、在庫が増えたにもかかわらず、アラートが出るという意図しない動作が発生した。また、在庫関連のロジックは複数存在し、在庫アラートのロジックをあちこちに書かなければならない状況が生じた。
さらに、カルテの名寄せ(複数存在する同一人物のカルテを統合する処理)のケースでも問題が起きた。会計情報や予約情報などを統合する必要があり、名寄せロジックが各所に散在することで、コードの可読性や品質が低下した。いずれのケースでも、DRY原則(Don't Repeat Yourself:同じコードやロジックを複数箇所で繰り返し記述するのを避け、コードの再利用性や保守性を高めること)が守れなくなっていた。
DDDの適用と、オニオンアーキテクチャの採用による解決
これらの問題を改善するために、まずロジックを細かく分解し、凝集度を上げるアプローチを試みた。しかし、その結果、依存関係が複雑化し、どのロジックがどこから呼ばれているのかが不明確になることが増えた。これにより、ユースケースの一部を変更する際にどこを修正すべきかが分からなくなる問題が発生した。最終的に、どのアーキテクチャでも、変更箇所とその影響範囲が明確であれば問題は解決すると結論付けた。そして、ロジック層にはアプリケーションロジックとドメインロジックの2種類が存在するという理解に至った。畠中氏は「これはDDDの書籍などでも言及されていて、実際に達成すべき重要な課題だと感じました」と振り返る。
アプリケーションロジックは仕様に基づいたもので、例えばメディカルフォースではこういうロジックがあるが、競合のプロダクトでは異なるといったものが該当する。アプリケーションロジックは、仕様に基づいて変更が容易にできる。一方、ドメインロジックは知識として蓄積され、メディカルフォースでも競合のプロダクトでも共通となる要素が含まれる。この2つを明確に分けることを意識した。
例えば、在庫アラートのケースでは、アラート通知というドメインロジックを用意しこちらに実際の処理を書いておく。それをアプリケーションロジックである在庫使用ロジックが呼び出す形にした。このようにすることで、高凝集・低結合を実現できる。また、アプリケーションロジックからはアプリケーションロジックを呼び出さず、ドメインロジックからはドメインロジックを呼び出さないというルールを守ることで、結合度を低くしながら高凝集を保った。カルテの名寄せ処理においても、患者の名寄せ処理をドメインロジックとし、アプリケーションロジックから呼び出すことでより効率的に管理できる。
畠中氏は、仕様変更だけでなく、改善活動についても話した。当初、フレームワークやORマッパーの変更は滅多にないと想定していたが、同社が活用するNode.jsの領域では、ORマッパーのデファクトスタンダードが変わってきていたり、発展したフレームワークも登場したりしている。フレームワークに依存しない設計方針をとっていたが、より変化に耐えられるように方針を転換した。また、データアクセス層については、エンティティの永続化と復元だけを担当させることを重視し、インフラストラクチャー層を設け、DBやORマッパーに対する処理を外側に配置した。
このように、高凝集・低結のためにさまざまなルールを設定したが、メンバーに周知徹底するのが難しいため最終的にオニオンアーキテクチャを採用した。ドメインモデルが中心にあり、ドメインサービス層、アプリケーションサービス層がアプリケーションの核となり、その周りさらにユーザーインタフェースやインフラストラクチャー、テストがある。
畠中氏はその効果について「結果として、仕様変更やテストがかなり楽になりました。当初は新規実装の速度が少し遅くなるのではないかと懸念していましたが、長期的に見ると、コードの再利用性が高まり、開発スピードも向上しています。これが非常に良かったと思っています。この経験を踏まえて、DDDやオニオンアーキテクチャについて考えていただけると幸いです」と語った。