DMM.comの、一番深くておもしろいトコロ。

本人確認システムをリプレイスした話

本人確認システムをリプレイスした話

  • このエントリーをはてなブックマークに追加

はじめに

この記事は、DMMグループAdvent Calendar 2022の15日目の記事です。

こんにちは。DMMのプラットフォーム(以下PFと略称)事業本部所属の佐々木勝春と申します。

この記事では私が参加しているチームで今年行なった本人確認システムをリプレイスした話ついてご紹介させていただきたいと思います。 普段私はDMMのPFのサーバーサイドのエンジニアとしてプラットフォームの一部の機能の開発・保守・運用などの業務を行なっています。

この記事の執筆動機としましてはDMM社内の業務に興味がある方へ向けて今年取り組んだ一つの業務の話を通して普段の開発の様子や開発環境周りの概要をご紹介できたらと思い執筆しました。

最後に宣伝もありますのでよろしければ是非ご覧ください。

本人確認システムリプレイスプロジェクトの概要

PFで現在取り組んでいるプロジェクトの1つに「Rabbitプロジェクト」というものがあります。 DMMは20年以上の歴史のある会社で、その分、昔からあるシステムが現在まで長い期間継続して使用されるケースが少なくありません。それにより、機能追加したいといった際にシステムの改修に時間がかかってしまったり、古いシステムの仕様が特定の人しか分からず、その人依存の対応でボトルネックになってしまったりなどのような課題がありました。 そこで数年前からPFで「負債脱却」という文脈でシステムのリプレイスに取り組んできましたが、その間も差し込み対応や運用保守などでど思うようにリプレイスを進めることができていませんでした。

そこで昨年より「Rabbitプロジェクト」として、リプレイスをPFの高優先度のプロジェクトとして位置付け、採用も強化したりすることで集中的に取り組めるように目指してきました。今回ご紹介する本人確認システムのリプレイスもそのプロジェクトの一環として取り組んできました。

リプレイス前の本人確認システム

今回リプレイスを行なった本人確認システムはDMM競輪DMM百万長者DMM通販などのDMM内のサービスで使用されています。本人確認システムは10年以上前から使用されており、サーバーのOSが古くなっている問題や、逐次的に本人確認システムを利用するサービスが増えていった経緯のため、本人確認システムの使われ方が統一化されておらず複雑化していたり、さらにデータ管理の面の問題など複数の課題がありました。

▼リプレイス前のシステムの課題例

  • システムの老朽化に伴う課題
  • 本人確認システムの責務範囲が曖昧
  • 年齢確認手段が、事業部によって異なる
  • 利用可能な本人確認書類が多くて複雑(一部、単独では無く複数組み合わせる必要がある)
  • データが複数システムで二重管理されている

リプレイス後の製品原則

これからの課題の解決と共に、リプレイス後に目指す製品原則としてユーザが本人確認書類を提出後、"サービスを利用したい熱"が冷めることが無いように迅速に審査を行えるように目標にしました。 また、本人確認の審査でユーザーが却下された際に、何が原因で却下されたのかとその後のユーザーのとるべきアクションについて明確に伝えられるようになっていること、さらにリプレイス前に承認作業を行うDMM内のサポートさんの手動対応の作業の運用負担減や承認フローの改善などを目指しました。

この製品原則を実現するにあたり、今回eKYCのSaaSを導入しました。 外部SaaSを導入することで、法令変更や本人確認書類の仕様変更など外部要因への変化の追随部分を外部に移譲することができ管理の負担が減るためです。 加えて外部SaaS機能を活用することで、本人確認書類の確認作業を効率化や(後続の章を参考)、ユーザーの承認待ちの時間短縮によるサービス離脱の防止などを見込みました。

prtimes.jp

リプレイス後のシステム構成

今回リプレイスするにあたりサーバーをオンプレから社内のマイクロサービスアーキテクト(以下MSAに略称)チームが開発・運用しているk8sクラスター基盤にリプレイスしました。 サーバーやバッチなどのリソースはk8sクラスター基盤上に、DBなどその他のリソースは各チームが保有するAWSアカウントで管理するような構成になっています。

システム概要図(外部通信などを簡略化)

k8sクラスター基盤を利用することでインフラシステムの構築工数を削減できたり、k8s基盤側に構築済みのCDをそのまま利用できたりログや監視用のインフラ構築が不要になる恩恵を受けることができ、アプリケーションの開発に集中して取り組めました。

DMM PFのMSAチームの取り組みやk8sクラスター基盤の詳細は、下記の去年のアドベントカレンダーでのMSAチームの方の記事をご参考ください。

inside.dmm.com inside.dmm.com

また12日目の記事でMSAチームの中村さんがk8sクラスター上の負荷試験基盤について執筆予定ですのでよろしければご覧ください。負荷試験も各チームで用意する必要がなかったため試験実施自体に集中して取り組むことができました。

また、CDツールに使われているArgoCDのResource HooksPostSync HookをトリガーにしてE2Eを自動実行するような仕組みを整備していたりします。

リプレイス作業の内容

PHPからGoへのリライト

アプリケーションのコードに関して、リプレイス前はPHPで実装されていたものをGoにリライトしました。GoはPFのマイクロサービスプラットフォームでのデファクトの技術になっていて採用面なども加味して採用されてます。 アプリケーションコードのリプレイスに関しては今回は承認フロー自体の更新もあり仕様変更で0からの実装になりました。Go言語の特徴であるシンプルな面と統一された書き方が今回のチーム開発時においてもマッチしていました。

サポートの確認業務の簡略化

これまではサポートさんが全チェック項目を目視確認を行っていた作業を、 SaaSのeKYCサービスのLIQUID eKYCを利用して、ユーザーが提出した本人確認書類のOCR読み取りとスコアリング分析などを利用してサポートさんの確認業務を簡略化できるようになりました。

既存システムからのデータ移行

既存システムからのデータを新システムに移行するにあたりインフラ面でもk8sのリソースのCronJobを利用しました。k8s環境とオンプレサーバーの疎通では既にPFのMSAチームとインフラ部で環境を構築されていたため、今回のデータ移行用のインフラ環境の構築自体は移行元のオンプレーサーバー側の疎通設定などだけで済み、スムーズにできました。

難しかったのがデータ構造の理解の部分で、初見のシステムだったため、複数のシステムにデータが分散しているシステム構成の理解や、既存システムのデータ遷移を理解するまでが大変でした。

テストの強化

実装完了後に結合テストを実施した際、それまでのテストでは検知できてなかった次のようなエラーが見つかりました。

タイプ 件数
仕様考慮漏れ 8
テスト漏れ 7
RC環境以上の環境設定漏れ 3
validation強化 2
ケース漏れ 1

二番目に多かった「テスト漏れ」に関して、テストコードによる自動テストの単体テストやモジュール内結合テストのテストケースのパターンを増やすことで品質強化を目指していきました。 各関数レベルでの単体テストの強化に加え、usecase層レベルでは各仕様を網羅するようにパラメータのケースも含めてテストをするようにしました。 さらにAPI全体のの統合テストのテストケースでも仕様レベルを網羅するようにテストを追加しました。 外部ライブラリに依存してテストしにくい箇所以外はテストが網羅できていて全体でも90%以上のCoverageになっています。

timeのモック

単体テストを強化する上でテストの実装を工夫した一例として、現在時刻に依存するテストを行いやすいように標準パッケージのtimeを使うのではなく プロジェクト内にinterfaceを定義して、それをDIしてテスト内で実装を差し替えることで対応してました。

func main() {
    ...
 
    timeUtil := lib.NewTime()

    ...

    hogeUseCase := usecase.NewHoge(
        hogeRepository,
        timeUtil,
    )
}

▲ main.go

package lib

import "time"

type (
    clock struct{}
    Time  interface {
        Now() time.Time
    }
)

func NewTime() Time {
    return clock{}
}

func (clock clock) Now() time.Time {
    return time.Now()
}

▲ lib/time.go

テスト側ではDIするtimeのinterfaceをmock関数に差し替えることでケース毎に時間に依存するテストをしやすくしています。

package usecase

import (
    "context"
    "testing"
    "time"

    "github.com/xxx/output"
    mocksTime "github.com/yyy/test/mock/lib"
    "github.com/yyy/test/mock/repository"
    "github.com/golang/mock/gomock"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/suite"
)

func TestHogeUseCaseSuite(t *testing.T) {
    suite.Run(t, new(HogeCaseSuite))
}

type HogeUseCaseSuite struct {
    suite.Suite

    ctx            context.Context
    repositoryMock *repository.MockHoge
    timeMock       *mocksTime.MockTime
}


func (h HogeUseCaseSuite) TestHoge() {
    type want struct {
        output *output.HogeDTO
        err    error
    }

    tests := []struct {
        name string
        mock func()
        want want
    }{
        {
            name: "正常時、値を取得できること",
            mock: func() {
                // テストで使用した値をここで設定
                h.timeMock.EXPECT().Now().Return(time.Now()).AnyTimes()
            },
            want: want{
                output: &output.HogeDTO{ID: "xxx"},
                err: nil,
            },
        },
        ...
    }

    for _, tt := range tests {
        h.Run(tt.name, func() {
            tt.mock()

            output, err := NewHogeUseCase(h.repositoryMock, h.timeMock).Get(h.ctx)
            if tt.want.err == nil {
                assert.Equal(ms.T(), tt.want.output, output)
                assert.NoError(h.T(), err)
            } else {
                assert.Nil(h.T(), output)
                assert.EqualError(h.T(), err, tt.want.err.Error())
            }
        })
    }
}

▲ usecase/hoge.go

属人化の防止

「Rabbitプロジェクト」でご紹介した通り、長く使われているシステムの知識が特定の人に偏ってボトルネックになってしまうなどがありました。

さらに、DMMは組織が大きいが故に、スピードを上げるためにそれぞれのチームが1つの会社のように独立した単位でそれぞれプロジェクトに取り組んできていてサイロ化が進んでいました。結果としてスモールチームによる意思決定や行動の高速化の反面、組織の規模を梃子にした相乗作用がそこまで大きくありませんでした。(これらを経緯として前述のMicroservices Architect in DMM Platformにあるようにpospomeさんを中心に共通基盤の構築やコミュニケーション面で取り組んでこられています。)

加えて、コロナ禍でのリモートワーク化になり、直接のコミュニケーション機会が減り、他チームで面識がない人とのコミュニケーションの心理的ハードルが低くはない状況がありました。

そこでk8sクラスター基盤利用チーム側での取り組みとして、直接携わってないチーム内のメンバーや将来的にjoinが想定されるメンバーへの共有として、技術選定から仕様、さらにポストモーテムなどの知見の共有(意思決定の経緯をログに残りしたりなども)をドキュメント化することを実装のタスクと並列で取り組んできました。

チーム内ドキュメントツリー例

コミュニケーション面ではチームとして小さなことでもまずは共有・コミュニケーションをとる文化を作るために SlackのPFチャンネルでコミュニケーションの促進に取り組んでいます。

Slackチャンネルでの共有の様子

リプレイス後の改善

ユーザーの承認待ち時間の改善

業務上の都合でシステムの構成は割愛させていただきますが、 本人確認書類のチェック作業をLIQUID eKYCを活用することでユーザーの承認待ち時間を最短で10分ほどに短縮できるようになりました。

サポートさんの運用業務の負担減・効率化

具体的な数値は割愛しますが、サポートさんの概算として、 1件あたりに承認作業に用意していた時間がリプレイス前と比べて半分ほどに短縮できるようになりました。

システム改修/システムの老朽化に伴う課題

サーバーの老朽化対応は今回のリプレイスで直近の課題はクリアすることができました。 アプリケーションコード面では、初期リリース後も継続してリファクタリングやテストの強化、運用面での自動化の導入などを通じてより変更しやすい、拡張性のあるシステムを目指して現在も継続して開発しています。

システム改修に関しては終わりがなく継続的なもののため、将来的にさらにGoから別の言語へのリプレイスや本人確認の仕様変更に対応できるようにアーキテクチャ面などで継続して取り組んでいきます。

その他、本人確認作業のフローの整備と承認基準のサービスでの統一が今回ででき、リプレイス後に新規で本人確認システムを利用したいというサービスにも対応することができました。

最後に

DMM PF中の一チームで今年取り組んだ業務の一つについてご紹介させていただきました。 ここに紹介した内容は執筆中も日々改善を続けていっております。ユーザーがDMMを楽しく利用できるように少しずつ継続的に取り組んでいきたいと思っています。

宣伝

今回紹介させていただいた本人確認システムのリプレイスはプロジェクトの他に、3900万人を超えるDMMのアカウントのサービスをリプレイスするプロジェクトやその他様々なプロジェクトを進行・予定しています。 今後さらにより多くのプロジェクトを進めていくにあたり協力してくださる方を現在募集しております。ご興味がある方はカジュアル面談も含めてよろしければご応募ください。

dmm-corp.com

余談

2022年9月1日よりポイント活用サイトの「モッピー」でDMMポイントとの交換ができるようになりましたのでよろしければこちらも是非ご利用ください。

pc.moppy.jp