SmartHR Tech Blog

SmartHR 開発者ブログ

プロダクト間のデータ連携によってSmartHRのプロダクト開発がもっと面白くなりそうな話

プロダクト間のデータ連携によってSmartHRのプロダクト開発がもっと面白くなりそうな話

このエントリは、SmartHR Advent Calendar 2023 シリーズ2の20日目の記事です。

こんにちは!SmartHRでプロダクトエンジニアとして配置シミュレーション機能を開発している新奥です。 この記事では、SmartHR内のプロダクト連携基盤が構築され、別のプロダクトのデータを気軽に取得できるようになったことにより、プロダクト価値と開発者体験の両方の向上に繋がっている話をご紹介します。

これまでのSmartHRの課題

前提を共有するために少しだけSmartHRのアーキテクチャの説明をします。

SmartHRは「基本機能」と呼ばれる1つの大きなRailsアプリケーションと、「分析レポート」「従業員サーベイ」などの複数の「オプション機能」という別個のRailsアプリケーションによって構成されています。基本機能側に REST API のエンドポイントを設け、オプション機能がそれを利用して動作しており、データベースを共有しているわけではありません。

SmartHRの簡易構成図
SmartHRの簡易構成図

このような構成にすることで、ソースコードの肥大化・複雑化を抑える、チーム規模が大きくなりすぎることによるコミュニケーションコストの増大を防ぐ、というメリットがある反面、課題もありました。

それは「複数のサービスにまたがるデータを1つのアプリケーション内に集約する簡単な方法がない」ということです。基本機能が持つ従業員データ・人事評価の最終結果・従業員サーベイの結果の3つのデータを同時に参照する必要があるときに、これらのデータは別々のアプリケーションで分散して保有しているため、それぞれのアプリケーションでAPIを提供してもらわなくてはなりませんし、受け取り側のアプリケーションでもAPIエンドポイントから受け取るための実装が必要になります。3つぐらいならなんとかなるかもしれませんが、連携先が増えるたびにAPIを追加していくのは開発者にとってはなかなか辛そうな未来が待っていそうですよね。

従業員データプラットフォームの誕生

実は今年、その課題を解決するためのソリューションがSmartHRに誕生しました。その名も「従業員データプラットフォーム」です。

それぞれのアプリケーションに蓄積された従業員データの集約と、そのデータを有効活用するための連携基盤の提供を目的としたこのプラットフォームは、2022年春頃から試験実装が始まり、2023年1月にプロダクト基盤チームというプロダクト同士の連携や共通化を担うチームが組成されたことで、各アプリケーションで活用可能になるための準備が着々と整ってきました。

詳しい使用技術に興味がある方はプロダクト基盤チームのメンバーが書いたテックブログ(SmartHRのマルチアプリケーションに分散した従業員データを集約する)も読んでみてください。

配置シミュレーションでの開発事例

そして、いよいよ従業員データプラットフォームを本番環境でも活用可能になったタイミングで先陣を切ったのが、オプション機能の1つであり、私が開発に携わっている「配置シミュレーション機能」でした。

従業員データプラットフォームを利用することになった背景

配置シミュレーションはその名の通り、SmartHRに蓄積された人事データを活用しながら、直感的な操作で誰でもかんたんに配置のシミュレーションができるアプリケーションです。 人員配置は生産性向上や人材育成に関わる重要度の高い業務であると同時に、個人と組織、短期と長期を複合的に見据える必要のある複雑度の高い業務でもあります。

そのような複雑度の高い業務ニーズに対応するため、基本機能のデータと従業員サーベイ・スキル管理などのオプション機能のデータを組み合わせて配置を検討する機能の提供を求められてきました。それを実現するために、従業員データプラットフォームを利用する最初のアプリケーションとして、配置シミュレーションに白羽の矢が立ったわけです。

従業員サーベイとの連携

配置シミュレーションが最初にデータ連携の対象としたのは「従業員サーベイ」でした。 この連携によって従業員サーベイ上で保有しているエンゲージメントサーベイの結果を見ながら、配置シミュレーション上で配置検討をできるようになり、離職防止・人材育成に繋がる配置を実現しやすくなるという価値を提供できるようになります。

実装の流れ

従業員データプラットフォームは実態としてはGraphQL API をひとつに統合するApollo Federationという技術を採用しており、そのAPIをクライアントとして利用するためにgraphql-rubygraphql-clientをgem installしました。

その上で、従業員データプラットフォームと通信するためのEmployeePlatform::ClientというClassを追加します。

require "graphql/client"
require "graphql/client/http"

module EmployeePlatform
  class Client
    attr_accessor :graphql_client

    def initialize
      uri = URI.parse "https://従業員データプラットフォームのGraphQLエンドポイント"

      http = GraphQL::Client::HTTP.new(uri) do
        define_method(:connection) do
          Net::HTTP.new(uri.host, uri.port).tap do |client|
            client.use_ssl = uri == "https"
            client.open_timeout = 10
          end
        end
      end

      schema = GraphQL::Schema.from_definition("config配下に配置したGraphQLのスキーマの一覧")

      @graphql_client = GraphQL::Client.new(schema:, execute: http)
      @graphql_client.allow_dynamic_queries = true
    end

    def parse(...)
      @graphql_client.parse(...)
    end

    def query(...)
      @graphql_client.query(...)
    end
  end
end

クエリを組み立てるためのEmployeePlatform:: QueryBuilderも追加します。

module EmployeePlatform
  module QueryBuilder
    class CrewQuery

      def initialize(attribute_names:)
        @attribute_names = attribute_names
      end

      def build
        query = <<-"GRAPHQL"
          query($crewId: ID!) {
            crew(id: $crewId) {
              id
              %s
            }
          }
        GRAPHQL

        query % attribute_fields.join(" ")
      end

      private

      def attribute_fields
        @attribute_names.map { send(_1) }.uniq
      end

      def engagements
        <<-"GRAPHQL"
          engagements {
            id
            name
            requestedAt
            answeredAt
            tags {
              name
              score
            }
          }
        GRAPHQL
      end
    end
  end
end

あとは従業員サーベイのデータを使いたい箇所で下記のように実装します。

client = EmployeePlatform::Client.new
crew_query = EmployeePlatform::QueryBuilder::CrewQuery.new(attribute_names: ['engagements']).build
client.query(client.parse(crew_query), variables: { crewId: "対象従業員のid" }).data.crew.engagements.map(&:to_h)

以上により、対象従業員のエンゲージメントサーベイの結果一覧を下記のようなハッシュの配列で取得することが可能になりました。

[
  {
    "id"=>"XXXXXXXXXXXXXXXXXXX",
    "name"=>"2023年10月エンゲージメントサーベイ",
    "requestedAt"=>"2023-10-27T15:44:43+09:00",
    "answeredAt"=>"2023-10-27T15:48:54+09:00",
    "tags"=>[
      {"name"=>"仕事の負担", "score"=>6.25},
      {"name"=>"成長の機会", "score"=>2.5},
      {"name"=>"アウトカム", "score"=>3.75},
      {"name"=>"離職意思", "score"=>2.5},
    ]
  },
  {
    "id"=>"XXXXXXXXXXXXXXXXXXX",
    "name"=>"2023年4月エンゲージメントサーベイ",
    "requestedAt"=>"2023-10-27T15:44:43+09:00",
    "answeredAt"=>"2023-10-27T15:48:54+09:00",
    "tags"=>[
      {"name"=>"仕事の負担", "score"=>5.00},
      {"name"=>"成長の機会", "score"=>2.75},
      {"name"=>"アウトカム", "score"=>3.75},
      {"name"=>"離職意思", "score"=>4.5},
    ]
  },
]

衝撃のデータ連携の手軽さ

ここから、要件に合うように取得したデータを整形する処理やエラー処理などは必要になりましたが、従業員データプラットフォームとの連携部分についてはここまでに記載したコードがほとんど全てです。 SmartHRの構成上かなり大変だと思っていた「別プロダクトのデータを参照する」という実装が100行に満たないほどのサンプルコードで紹介しきれる規模の実装で済んだのですから、その手軽さは配置シミュレーション開発チームにとってはかなりの衝撃でした。

スキル管理との連携

その「データ連携の手軽さ」を物語るエピソードがあります。 従業員サーベイとの連携機能の次に取り組んだ「スキル管理」との連携機能を開発した際の話です。

スキル管理とは、その名の通り、従業員の保有するスキル・資格および研修受講データを一元管理できるようなオプション機能です。 業界によっては「1つの事業所には特定の資格を有する従業員を最低◯◯人以上配置しなければならない」という制約があるような場合もあるため、配置シミュレーションと親和性が高いデータを持っているアプリケーションであると言えます。

従業員サーベイ連携から2週間でのリリース

前述の従業員サーベイとの連携機能をリリースしたのが11月6日(月)だったのですが、スキル管理との連携機能はそのちょうど2週間後の11月20日(月)にリリースすることができました。

従業員サーベイから取得するデータの塊が1つから2つに増えたことによりバックエンド・フロントエンド両方で構造の見直しも行ったのですが、それでもこのスピード感で開発を進めることができたのは、従業員サーベイのデータとスキル管理のデータの両方を同じ従業員データプラットフォームという統一されたAPIから取得することができた、という要因が強かったように思えます。おそらく、それぞれのアプリケーションで別個でAPIが提供され、配置シミュレーション側でそれぞれのAPIに対応するようにfetch処理を実装するようなやり方ではここまでのスピード感を出すことは難しかったでしょう。

さらに、このタイミングで配置シミュレーション側の実装でも構造の見直しを行ったことにより、連携先が3つ、4つと増えてきても対応しやすい状態にできているため、次に連携先が増えるタイミングではもっとスピーディーに開発を行えるかもしれません。このスケーリングのしやすさは、まさに従業員データプラットフォームによる統一化の恩恵と言えるでしょう。

開発組織が一丸となって取り組んだ新機能

ここで忘れてはいけないのは、これらの新機能のリリースは決して配置シミュレーション開発チームだけの功績ではなく、使いやすい従業員データプラットフォームを構築してくれたプロダクト基盤チームと、そのプラットフォームに対してデータを連携するための導線を用意してくれた従業員サーベイ開発チーム及びスキル管理開発チームの事前の援護があってこそのものである、ということです。

まさにSmartHR開発組織が一丸となって実現した新機能であると言え、これらの機能を無事にリリースできたことは私たち開発者にとって大変意義があることでした。

機能についてもっと知りたい人向け

もしかしすると機能の詳細について興味を持ってくれた方もいるかもしれないので、配置シミュレーションを担当するプロダクトマーケティングマネージャーが書いた解説記事もご紹介させていただきます。ビジネス寄りの内容なのでテックブログではなく、SmartHR Mag.という弊社が運営する人事・労務に関わるメディアの記事なのですが、とてもわかりやすい内容になっていますのでぜひご覧ください。

従業員データプラットフォームを活用して今後やっていきたいこと

ここまで従業員データプラットフォーム上のデータ連携によって配置シミュレーションでの開発の幅が広がった話をしてきましたが、開発チームとしてはまだまだやりたいことがたくさんあるので、最後にそれを紹介してこの記事を締めたいと思います。

データの一括取得

今のところ、ユーザーは配置シミュレーションの画面上に表示される一人一人の従業員カードを押下し個別の従業員詳細情報を表示させたタイミングではじめて従業員サーベイ及びスキル管理のデータを参照できるようになっています。一方で、配置シミュレーションのデータベース内に保持しているデータについては画面上に表示されている全従業員について一括で参照することができるので、従業員データプラットフォーム経由のデータも同様の表示のさせ方をできるようにしたいと思っています。

一括で従業員データプラットフォームからデータを取得するにあたって、画面描画が遅くなってしまうなどのパフォーマンスの懸念がないようにクエリ設計やキャッシュ戦略を考えていきたいです。

検索・絞り込み

データの一括取得ができるようになった後は、例えば「離職リスクの高い従業員だけに絞って画面上に表示する」等の従業員データプラットフォーム経由のデータを使った検索・絞り込み機能を追加したいと考えています。

これについては、従業員データプラットフォームから取得したものを配置シミュレーション内で検索するのが良いか、配置シミュレーションから検索条件を従業員データプラットフォームに渡して検索結果を返してもらうのか、などの議論をプロダクト基盤チームと配置シミュレーション開発チームが協力しながら進めていくことになるかなと思います。

基本機能と配置シミュレーション間のデータ同期のワーカーが不要な世界線

現在の配置シミュレーションの構成では、従業員データプラットフォーム経由のデータはリアルタイムで反映されますが、それ以外の大部分のデータはユーザーが任意のタイミングで実行する非同期ワーカー処理が基本機能のREST APIを叩いてからでないと反映されません。

この非同期処理が企業規模によっては数十分かかることもあるため、一部のユーザーに煩わしさを感じさせてしまっている恐れがあります。データの取得方法を従業員データプラットフォーム経由での取得に寄せることでこの非同期ワーカー処理自体を不要にし、リアルタイムで参照可能にすることでその煩わしさを解消したいという展望があります。

We Are Hiring!

いかがでしたでしょうか? 従業員データプラットフォームのような全社横断的な仕組みを作りたい人も、その仕組みを使って価値の高い人事労務プロダクト・タレマネプロダクトを作りたい人も、SmartHRでは一緒にSmartHRを作りあげていく仲間を全方位積極採用中です!

少しでも興味を持っていただけたら、カジュアル面談でぜひお話しましょう!

hello-world.smarthr.co.jp