Kafkaとルールエンジンを活用した決済システム構築の紹介
こんにちは!アイムデジタルラボの採用広報担当です。今回は、最近リリースされた決済システムについて、エンジニアメンバーに記事を書いてもらいました。ぜひ最後までご覧ください!
プロジェクトの背景
三越伊勢丹では、現在でも店頭での購買が一般的で、補完的にECサイトが存在しています。一方で、DXを推進する中では、店頭やECサイトとは異なる新たな購買体験の整備も進めています。例えば、チャットを用いたリモート購買であれば、どこにいても、どんな商品でも、接客を受けながら購入することができます。この取り組みの一環として、新たな決済システムの開発に着手しました。
システム的に解決すべき課題
三越伊勢丹で新たな決済システムを作るにあたって、最大の課題は百貨店特有の様々な決済手段と値引きや優待ルールの存在です。クレジットカードに加え、ポイントや積立といった独自の仕組み、そして複雑な値引きや優待のルールがあります。そのため、以下のような問題が想定されました。
1つ目は金額計算です。商品の種類やお客さまの属性によりルールの適用が変わるため、複雑なロジックが求められます。実際、店頭のPOSレジやECサイトでも金額計算の保守性は大きな課題となっています。
2つ目は決済確定処理です。先述したように、決済手段は多種多様であり、お客さまの選択により組み合わせ数も膨大となります。また、一部のシステムは店頭POSレジからの利用を前提としているため、夜間では制御が異なるという特性も存在します。
全体の構成
そこで、決済システム内で受注処理を次のように構成しました。中心となるのは受注確定処理で、ここに複雑なロジックを集約しています。ただし、金額計算に関する部分は金額計算エンジンに切り出してあります。そして受注確定後に、その結果を利用して決済確定処理を実行するようにしました。
この記事では、金額計算と決済確定について詳細を説明していきます。
金額計算におけるルールエンジンの活用
金額計算にはルールエンジンの採用を検討しました。これは、複雑な金額計算をモデル化できれば、保守性が高くなると考えたためです。このため、ルールの整理には、開発仕様標準化団体OMGが定義するビジネスルール定義モデルの一つであるDMN(Decision Model and Notation)の中で、DRD(Decision Requirements Diagram)を活用しました。
金額計算のために作成したDRDは以下の通りです。四角い箱が「ディシジョン」で、どのような判断をするのかを定義しています。各ディシジョンに矢印で繋がっている角丸が「入力データ」を示します。
DRDでは最終的に得たい結果を最上部に置き、そのために必要な要素を下に向かってブレイクダウンしていきます。この場合は「請求金額」が最終的に得たい結果であり、そのためには請求金額計算が必要で、そこには税額合算の結果が必要だ、というように読みます。そのため、実際の計算処理は逆に下から実行され、下のディシジョンの出力パラメタが上のディシジョンの入力パラメタとなります。
詳細な仕様はディシジョン単位で定義していきます。特に重要となるのは、入力パラメタと出力パラメタの定義です。前述の通り、下のディシジョンの出力パラメタが上の別のディシジョンの入力パラメタとなるため、それらが矛盾なく整合性を持つように何度も確認を行いました。
実装にはRed Hat Decision Managerを使用しました。中身はオープンソースのルールエンジンKogitoです。Kogitoの特徴の一つは、ディシジョンテーブルをExcelでも記述できる機能です。
Excelでルールを記述する場合は、各行にルールを表現します。入力パラメタの組み合わせによって、どの行のルールが適用されるかが決まり、必要な計算が行われます。例えば、ある商品の税区分や税率によって、集計式が異なるといった事項を整理し、記述が可能です。
ディシジョンをコード形式で表現すると、IF文の階層構造になってしまうため、全体的な見通しが悪くなりがちです。一方、テーブル形式であれば、何を利用して何を判断しているのかを容易に理解できます。
このExcelは、ビルド時にJavaクラスに変換され、JAR形式になります。これをコンテナ化して、APIサーバとしてデプロイします。受注確定処理は、支払い方法に応じて必要なパラメタを組み立て、金額計算エンジンのAPIを呼び出し、金額計算結果を取得します。
こうした仕組みを採用した結果、テストの自動化が容易に可能になりました。テストは、入力パラメタと予想される出力パラメタをExcelで定義し、それをエンジンに掛けることで実施できます。テストはディシジョン単体と、組み上げられた金額計算全体に対して行われます。金額計算全体での計算結果は店頭のPOSレジと同じになる必要があるため、POSレジのテストデータと結果を借りてきて、それをExcelに転記し、現新比較を行うことで品質を担保しています。
なお、金額計算エンジンはグループ会社である三越伊勢丹システム・ソリューションズの若手社員のチームが内製開発しました。これまでPOSレジの開発に関わったことがないメンバーばかりだったので、現状の仕様の理解やDRDの整理には、かなりの努力が必要でしたが、全体のモデルが安定してからは順調に進んだと思います。品質もですが、保守性が非常に高く、話題のインボイス対応も小さな工数で対応できました。
決済確定におけるKafkaの活用
次に、決済確定処理について説明します。前述の通り、百貨店では多様な決済手段が存在し、それぞれに対向システムがあります。そして、支払い方法による組み合わせが多く、今後も決済手段の追加や変更が予想されます。そこで、高凝集・疎結合の原則を念頭に、決済手段ごとの処理を別モジュールに分割し、それらを疎結合に組み合わせるような仕組みを検討しました。
まず、受注確定処理では、お客さまの属性や支払い手段に応じて、どの順番で、どのような決済処理を行うのかを判断し、それらを含んだ決済依頼メッセージを作成します。このメッセージには、決済手段の実行順序と、必要な情報が設定されます。例えば、決済A→B→Cと実行する必要がある場合、決済依頼メッセージにはABCという順序と、次に処理すべき対象として「A」が定義されます。
決済確定処理は、この決済依頼メッセージに従ってそれぞれの決済処理を実行します。この実行制御には非同期ストリーミング処理を活用しています。決済依頼トピックにメッセージが流れると、各決済コンポーネントは一斉にメッセージを受け取りますが、「A」のコンポーネントだけが決済処理を実施します。処理が完了すると、順序に従って次に処理すべき対象(ABCの場合は「B」)を設定し、再度、決済依頼トピックにメッセージを流します。そうすると、今度はBの処理が行われます。
このように、各決済処理は、自分が対象となっているメッセージが来た時だけ決済処理を実施し、次の誰かに引き渡しますが、それ以外の時には無視します。これにより、個別の決済処理が支払い手段や決済の順序性から完全に独立させることができました。
ただし、非同期処理にはエラーハンドリングの複雑さが伴います。個別の決済処理でエラーが発生しても基本的にはリトライしますが、リトライでの復帰が望めないようなエラーの場合には、メッセージをデッドレタートピック(DLT)に流します(参考:Dead Letter Channel)。DLTの先にはエラー用のコンシューマが存在し、エラーメッセージを永続化した上で、開発メンバーに通知します。今後は、一部のエラーに対しては自動リカバリも適用していきたいと考えています。
非同期メッセージングにはKafkaを利用しているので、実行基盤にはAWSが提供するMSK(Amazon Managed Streaming for Apache Kafka)Serverlessを採用しました。以前からMSKを活用していましたが、Serverless版の提供開始により、さらに可用性と運用性が向上したと感じています。
決済確定処理の開発では、連携先の決済システムの仕様や特性を理解するのにかなりの時間を要し、同時にKafkaを利用した設計についても何度も議論を重ねました。その結果、各決済処理をシンプルに実現でき、開発の生産性も向上させることができたと感じています。
まとめ
決済システムは1ヶ月をかけて段階的に適用範囲を広げ、現在では全面稼働に移行しています。複雑な連携があるものの、性能としては想定通りの結果を発揮できています。そして、それ以上に決済システムとしての可用性と保守性が大幅に向上したことは、大きな成果だと考えています。今後も、新たな購買体験を創造する中で、決済についてもさまざまな変更が予見されますが、今回の決済システムであれば、十分に対応が可能と考えています。
いかがでしたでしょうか。アイムデジタルラボでは、新しい技術を活用したDXの推進をしています。ご興味のある方は、ぜひお気軽にご応募ください!
Wantedly掲載の求人情報はこちら!