Upgrade to Pro — share decks privately, control downloads, hide ads and more …

replace of cart system on ZOZOTOWN

replace of cart system on ZOZOTOWN

TakahashiKazutaro

March 24, 2023
Tweet

More Decks by TakahashiKazutaro

Other Decks in Technology

Transcript

  1. ZOZOTOWNカート決済システムのリ
    プレイス〜歩みとこれから〜

    2023-03-23 ZOZO Tech Meetup〜Java活用事例紹介〜

    株式会社ZOZO

    技術本部 カート決済部 カート決済基盤ブロック

    高橋 和太郎
    Copyright © ZOZO, Inc.
    1

    View Slide

  2. © ZOZO, Inc.
    株式会社ZOZO

    技術本部 カート決済部 カート決済基盤ブロック
    高橋 和太郎

    2019年よりZOZOTOWNリプレイスプロジェクトに参画し、
    2021年よりカート決済システムリプレイスに従事。趣味は猫
    と遊ぶこと。

    2

    View Slide

  3. © ZOZO, Inc.
    https://zozo.jp/

    ● ファッションEC

    ● 1,500以上のショップ、8,500以上のブランドの取り扱い

    ● 常時90万点以上の商品アイテム数と毎日平均2,600点以上の新着 商品
    を掲載(2022年12月末時点)

    ● ブランド古着のファッションゾーン「ZOZOUSED」や

    コスメ専門モール「ZOZOCOSME」、靴の専門モール

    「ZOZOSHOES」、ラグジュアリー&デザイナーズゾーン

    「ZOZOVILLA」を展開

    ● 即日配送サービス

    ● ギフトラッピングサービス

    ● ツケ払い など

    3

    View Slide

  4. © ZOZO, Inc.
    4
    Agenda

    ● カートリプレイスについて

    ● カートリプレイスPhase1

    ● カートリプレイスPhase2

    ● 技術選定・ツール

    ● 部内の取り組み

    ● リプレイスの今後


    View Slide

  5. © ZOZO, Inc.
    5
    カートリプレイスについて


    View Slide

  6. © ZOZO, Inc.
    カートリプレイスのこれまで

    Qiita: ZOZO開発組織の2022年の振り返りと現状
    6
    ※ 2022年12月公開時点

    View Slide

  7. © ZOZO, Inc.
    カートリプレイスの目的

    成長し続けるZOZOTOWNを支え、お客様にいつでも快適に買い物をしていただけるサービスを提供
    するため

    ● セールなどの高負荷イベントに対応できること

    ○ スケール可能であること

    ○ キャパシティコントロールが可能であること

    ● Datadog、SentryなどのSaaSを利用した運用監視の効率化ができること

    ● CI/CDなどを取り入れ、開発生産性を向上できること

    ● レガシー技術をモダン化できること

    7

    View Slide

  8. © ZOZO, Inc.
    8
    カートリプレイスPhase1


    View Slide

  9. © ZOZO, Inc.
    カートリプレイス前の構成概要

    ● リクエストをIISで受け、VBScriptからSQL Serverのストアドプロシージャを呼び出し、処理を実行

    ● ストアドプロシージャでは、在庫の引き当てとカート投入処理を実施

    9

    View Slide

  10. © ZOZO, Inc.
    構成概要

    TECH BLOG:ZOZOTOWN カート決済機能リプレイス Phase1 〜 キャパシティコントロールの実現

    10

    View Slide

  11. © ZOZO, Inc.
    Cart Queuing Systemの概要

    11

    View Slide

  12. © ZOZO, Inc.
    12
    カートリプレイスPhase2


    View Slide

  13. © ZOZO, Inc.
    構成概要(Before)


    13

    View Slide

  14. © ZOZO, Inc.
    構成概要(After)


    14

    View Slide

  15. © ZOZO, Inc.
    Cart Stock Systemの概要

    ● 在庫情報テーブルで在庫を管理

    ● オンプレの在庫情報との整合性を担保するために在庫変更ログテーブルを使用

    15

    View Slide

  16. © ZOZO, Inc.
    16
    技術選定・ツール


    View Slide

  17. © ZOZO, Inc.
    技術選定・開発ツールについて

    社内のOSS利用ガイドラインに基づいて実施

    ● Javaのバージョン

    ● Spring Bootのバージョン

    ● ソフトウェアアーキテクチャ

    ● ビルドツール

    ● テストフレームワーク

    ● AWSの使用サービス

    ● ライブラリ

    ○ O/R Mapper

    ○ コードフォーマッター

    ○ DBマイグレーションツール

    ○ OpenAPI


    17
    開発に使用しているツール

    ● IntelliJ IDEA

    ● GitHub

    ○ GitHub Actions

    ■ 単体テスト

    ■ コードの自動フォーマット

    ■ 必要に応じてDocker Imageの作成

    ● Sentry

    ● Datadog

    ○ 監視

    ○ 分析

    ● SonarCloud

    ○ 静的コード解析

    カート決済専任のSREがいるため、インフラ面で気軽に相談ができる


    View Slide

  18. © ZOZO, Inc.
    検討中概要

    ● APIからSQL Serverのストアドプロシージャの呼び出しを想定

    ● Java + Spring Bootを使用して実現するためのライブラリの選定などを実施中

    18

    View Slide

  19. © ZOZO, Inc.
    技術選定

    要件

    ● APIからSQL Serverのストアドプロシージャを呼び出す

    ● ストアドプロシージャは複数のResultSetを返 す


    検証対象ライブラリ(O/R Mapper)

    ● JPA

    ● Spring Data JDBC

    ● MyBatis

    ● Doma 2


    比較内容

    ● レイテンシ

    ● コード量

    19

    View Slide

  20. © ZOZO, Inc.
    技術選定(JPA)

    20
    ● 平均レイテンシ:45ms

    ● コード量:

    ● 問題点:2つ目のResultSetを取得する
    ために2回目のDB接続が必要

    StoredProcedureQuery procedureQuery
    = createStoredProcedure(
    entityManager.createStoredProcedureQuery(
    "SampleProcedure", SampleProcedure.class));
    setParameterForProcedure(procedureQuery, id);
    // 1回目の実行
    if(procedureQuery.execute()) {
    // Outputパラメータ取得
    Integer outputId = (Integer) procedureQuery.getOutputParameterValue("OutputId");
    // 1つ目のResultSetの取得
    List firstResults = procedureQuery.getResultList();
    // 2回目の実行準備
    procedureQuery = createStoredProcedure(
    entityManager.createStoredProcedureQuery(
    "SampleProcedure", SampleProcedure.class));
    setParameterForProcedure(procedureQuery, id);
    List secondResults = new ArrayList<>();
    if(procedureQuery.hasMoreResults()) {
    // 2つ目のResultSetの取得
    secondResults = procedureQuery.getResultList();
    }

    View Slide

  21. © ZOZO, Inc.
    技術選定(Spring Data JDBC)

    21
    ● 平均レイテンシ:22ms

    ● コード量:他のライブラリに比べると多
    い

    CallableStatement callableStatement
    = connection.prepareCall("{call SampleProcedure(?,?)}");
    callableStatement.setLong("id", id.value());
    callableStatement.registerOutParameter("OutputId", Types.INTEGER);
    // 1つ目のResultSetの取得
    ResultSet firstResultSet = callableStatement.executeQuery();
    List firstResults = new ArrayList<>();
    while(firstResultSet.next()) {
    firstResults.add(new FirstResult(
    new Id(firstResultSet.getLong("Id")),
    new Name(firstResultSet.getString("Name"));
    }
    List secondResults = new ArrayList<>();
    if(callableStatement.getMoreResults()) {
    // 2つ目のResultSetの取得
    ResultSet secondResultSet = callableStatement.getResultSet();
    while(secondResultSet.next()) {
    secondResults.add(new SecondResult(
    new ItemId(secondResultSet.getLong("ItemId")),
    new ItemName(secondResultSet.getString("ItemName")));
    }
    }

    View Slide

  22. © ZOZO, Inc.
    技術選定(MyBatis)

    22
    ● 平均レイテンシ:149ms

    ● コード量:Repository層としての量は少ないが、XMLにストアドプロシージャ呼び出し用の定義を書くため、全体とし
    てはあまり少なくならない

    List> resultSet
    = procedureMapper.executeSampleProcedure(id.value(),name);
    // 1つ目のResultSetを取得
    List firstResultObjects = (List) resultSet.get(0);
    List firstResults = new ArrayList<>();
    for (Object obj: firstResultObjects) {
    FirstResult firstResult = (FirstResult) obj;
    firstResults.add(new FirstResult(
    new Id(firstResult.getId())),
    new Name(firstResult.getName());
    }
    // 2つ目のResultSetを取得
    List secondResultObjects = (List) resultSet.get(1);
    List secondResults = new ArrayList<>();
    for (Object obj: secondResultObjects) {
    SecondResult secondResult = (SecondResult) obj;
    secondResults.add(new SecondResult(
    new ItemId(secondResult.getItemId())),
    new ItemName(secondResult.getItemName());
    }
    namespace="jp.xxx.api.infrastructure.mappers.SampleProcedureMapper">
    type="jp.xxx.api.infrastructure.models.FirstResultSet">



    type="jp.xxx.api.infrastructure.models.SecondResultSet">



    resultMap="FirstResultSet,SecondResultSet"
    statementType="CALLABLE" >
    {call SampleProcedure(#{id,jdbcType=BIGINT,mode=IN},
    #{name,jdbcType=VARCHAR,mode=OUT})}


    View Slide

  23. © ZOZO, Inc.
    技術選定(Doma 2)

    23
    ● 平均レイテンシ:19ms

    ● コード量:ライブラリ内に必要なアノテーションが用意されており、コード量はかなり少ない

    ○ ドメインオブジェクトへの変換もアノテーションにより可能

    @Procedure(name = "SampleProcedure")
    void execute(
    @ResultSet(ensureResultMapping = true) List firstResults,
    @ResultSet(ensureResultMapping = true) List secondResults,
    @In Id id,
    @Out Reference outputId);
    import org.seasar.doma.Domain;
    @Domain(valueType = Long.class, accessorMethod = "value")
    public record Id(Long value) {
    }

    View Slide

  24. © ZOZO, Inc.
    24
    技術選定のまとめ

    平均レイテンシ コード量 複数ResultSetのDB接続回数
    Doma 2 19 ms 少 1回
    Spring Data JDBC 22 ms 多 1回
    JPA 45 ms 中 n回
    MyBatis 149 ms 多 1回

    View Slide

  25. © ZOZO, Inc.
    25
    部内の取り組み


    View Slide

  26. © ZOZO, Inc.
    部内でのJavaへの取り組み

    カート決済部

    ● 事業案件を担当するチーム

    ○ VBScriptを使用して開発

    ○ 今後リプレイスしたコードも管理していく

    ● リプレイスを担当するチーム


    事業案件を担当するチームでの取り組み

    ● Java未経験者もいたが、外部講師によるJava講習会を受講して基礎を身につけた

    ● チーム内でJavaを使用した勉強会を週に2回程度実施


    26

    View Slide

  27. © ZOZO, Inc.
    27
    リプレイスの今後


    View Slide

  28. © ZOZO, Inc.
    リプレイスのこれから

    Qiita: ZOZO開発組織の2022年の振り返りと現状
    28
    ※ 2022年12月公開時点

    View Slide

  29. © ZOZO, Inc.
    29
    一緒にカート決済部で働く仲間を募集しています


    View Slide

  30. View Slide