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

短納期でローンチした新サービスをJavaで開発した話/launched new service using Java

B887cc104275187588fae7d58790563e?s=47 A1
June 17, 2022

短納期でローンチした新サービスをJavaで開発した話/launched new service using Java

JJUG CCC 2022 Spring 発表資料

https://fortee.jp/jjug-ccc-2022-spring/proposal/3bf78003-4672-457c-a8fc-47adb4a1812e

概要 / Abstract:
電子帳簿保存法の改正により急遽立ち上がった新規サービス開発。
久々の新規開発でテックリードの腕の見せ所とはりきる私。
新しい技術要素をモリモリ使って開発したい..ただ法改正がトリガーなので絶対に納期が延ばせないが考えなければいけないことは盛り沢山
・使用するJavaのバージョンとフレームワーク
・マルチテナントDB方式
・APIクライアント
・セキュリティ関連
・多言語、タイムゾーン対応
・フロントエンド
・クラス設計の方針
などなど
そのような状況の中でJavaを中心とした技術選定で妥協しなかったことや開発で苦労したことなど、開発事例をお話させていただきます。

B887cc104275187588fae7d58790563e?s=128

A1

June 17, 2022
Tweet

More Decks by A1

Other Decks in Programming

Transcript

  1. Confidential All Rights Reserved.Copyright© 2022 RAKUS Co.,Ltd.
 サービス開発の理想と現実
 短納期でローンチした新サービスをJavaで開発した話
 2022/6/19

    JJUG CCC 2022 Spring
 1

  2. 自己紹介
 三田 英一<Eiichi Mita>
 株式会社ラクス 開発統括部
 <経歴>
 SIにてシステム運用・開発を経験した後、
 2014年に株式会社ラクスに入社
 ずっと1つのサービスの機能開発や運用を担当


    @eichisanden
 2

  3. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 3
 目次

  4. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 4

  5. はじめに
 2022年1月に電子帳簿保存法が改正されることになり、それに合せて急遽新サー ビスの開発プロジェクトが立ち上がりました。
 この発表は6ヶ月の短期間でJavaで新サービスを開発をした事例の紹介になりま す。
 
 法律が絡み競合他社も一斉に同じようなサービスをリリースしてくるので納期は 伸ばせない... ただ自社サービスとして長い間保守開発が続くので技術的な負債 を最初から作りたくない...


    この様な葛藤の中でどのように開発を進めたのかお話したいと思います。
 5

  6. 今回開発したサービス「楽楽電子保存」について
 6


  7. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 7

  8. 開発体制
 既存サービスを開発している2チームが開発
 8
 アルファ・チーム 6名 ブラボー・チーム 7名 Tech Lead(me)
 PM


    PL/PdM
 基盤部分を実装するの 1人じゃ無理!! ※実際のチーム名とは異なります

  9. 基盤チーム 5名 開発体制
 一時的に基盤チームを立ち上げ
 9
 アルファ・チーム 4名 ブラボー・チーム 5名 Tech

    Lead(me)
 PM
 PL/PdM
 開発チームからメンバーを借りて 
 WG的なチームで基盤部分を一気に作り上げた 
 大凡タスクが完了したら開発チームに人を戻した 

  10. 開発体制
 「チームトポロジー」という本にこのようなチーム分けの話がありました。
 (最近出た本なので読んだのは開発後ですが)
 10
 ❖ ストリームアラインドチーム
 ➢ 顧客に直接価値を届けるいわゆる開発チーム ❖ プラットフォームチーム


    ➢ インフラやツール、共通サービスなどを提供するチーム ❖ イネイブリングチーム
 ➢ 他のチームが技術やスキルを身につけるのを支援するチーム ❖ コンプリケイテッド・サブシステムチーム
 ➢ 複雑なサブシステムやコンポーネントを扱う専門チーム • 図らずも今回の基盤チームはプラットフォームチームとイネイブリン グチーム的な役割を担っていた
 • 現在、組織が拡大してチーム数が増えていっている状況の中、どう やって新しい技術を広めていくか悩んでいるところだったので非常に 参考になりました
 チームトポロジー
 価値あるソフトウェアをすばやく届ける適応型組織設計
 マシュー・スケルトン 著 マニュエル・パイス 著

  11. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 11

  12. Javaのバージョン
 • 既存サービスではJava11を使っている
 • 取り込めるどうか微妙なタイミングにJava17 LTSがリリースされた
  →Java11 or 17 さてどうする?


    
 12

  13. Javaのバージョン
 • サポート期間の観点ではどうか?
 ◦ Java11でもJava17でも十分長いサポート期間を提供してくれているベンダー がいるので決定的な判断ポイントにはならなかった 
 例)Amazon Correto
 13


    https://aws.amazon.com/jp/corretto/faqs/#support より引用
  14. Javaのバージョン
 • ツール類やMWは対応しているか?
 ◦ リリースサイクルが半年になった恩恵か?全然問題なかった ◦ IDEは商用のIntelliJ IDEAだったからかも(他は分からない) ◦ JacksonがRecordに対応していないのでは?と思ったが全然そんなことな

    かった 14

  15. Javaのバージョン
 • 言語仕様の差分は激しくないか?
 ◦ 戸惑うような大きな言語仕様の差分はなかった ◦ Java8から一気にジャンプアップだと教育が大変だったかも ◦ 使いたい機能追加は結構あった 15


  16. Javaのバージョン
 結論:Java17を採用した☺
 
 16
 • いつか17に上げるので移行コストが掛からない方が良い
 • 新しい言語機能が使えるのはDevEx的にGood
 • 学習コストはJava11と17の差分を説明したぐらいで軽微


  17. Javaのバージョン
 時間がなくて出来なかったこと😓
 
 17
 • 使ったことがないGCの検証
 ◦ 採用したかはともかく調査ぐらいはしたかった ◦ システム特性的に問題なさそうなことだけ確認してG1GCを選択

    • GraalVMを検証したかった
 ◦ フルネイティブ化出来なくても速くなるという話を聞いたので
  18. Javaのバージョン
 ⚠ 顧客向けクラウドサービスでは
 Oracle No-Fee Terms and Conditions (NFTC)
 の対象外なのでご注意ください


    
 ライセンス事項を確認することをお勧めします
 18

  19. Javaのバージョン
 17の良かった新機能☺ ※Java11との比較です
 • Record
 19
 /** * recordで書いた場合 */

    public record LoginId(long value) { public LoginId { if (value <= 0L) { throw new IllegalArgumentException(); } } } Immutableなクラスが短く書けて便利なのでValue Objectの実装で多用した。
 
 /** * classで書いた場合(今までの書き方) */ public class LoginId { private final long value; public LoginId(long value) { if (value <= 0L) { throw new IllegalArgumentException(); } this.value = value; } public long getValue() { return this.value; } }
  20. Javaのバージョン
 もしかしてrecordがあればlombokは不要では?🤔
 というチームメンバーから疑問の声が
 →そんなことはなかった
  確かに@Valueは使わないかもしれないが他のメソッドは使いたい
 20
 アノテーション 用途 @Slf4j Logger

    log = org.slf4j.LoggerFactory.getLogger(...);のショートハンド。 めちゃくちゃ使う @AllArgsConstructorなどコンストラクター系 コンストラクタでDIをインジェクションしたいのでめちゃくちゃ使う @NonNull 引数の必須チェックなどに使用する
  21. Javaのバージョン
 Java17の良かった新機能
 21
 • テキストブロック
 ◦ 個人的には長年待ち望んでいた機能 ◦ SQLを文字列に書いたりする機会が減って思ったよりは使用ケースは少なかった ◦

    テストコードではかなり使用した mockMvc.perform(MockMvcRequestBuilders.put("/user") .content("""{ "id": 1, "name": "Taro Yamada", "age": 23 }""").contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().json(expected, true))
  22. Javaのバージョン
 Java17の良かった機能
 • instanceofのパターンマッチング
 22
 そんなに多用するものではないが地味に便利
 private ContentCachingResponseWrapper wrapResponse(HttpServletResponse response)

    { if (response instanceof ContentCachingResponseWrapper wrapper) { return wrapper; } else { return new ContentCachingResponseWrapper(response); } }
  23. Javaのバージョン
 Java17の地味だけど良かった機能
 • stream.toList
 23
 List<String> list = List.of("", "a");

    List<String> list2 = list.stream().filter(a -> !a.isEmpty()).toList(); collect(Collectors.toList())は長いし直感的じゃないのですごく便利
 • NullPointerExceptionのエラーメッセージ改善
 String s = getS(); getNull(s.toUpperCase()).getBytes(); Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "s" is null at BasicCompletionDemo.main(Learning.java:7) 数珠つなぎに書いてたりすると、どこがnullなのか分からないことがあったが解消されている

  24. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 24

  25. フレームワーク
 結論:Spring Bootを選択した
 ◦ 現状、特殊な事情がない限りSpring Bootで良さそう ◦ 既存サービスではSpring MVCなのでbootJarがとても楽に感じた ▪

    Tomcatの面倒をインフラにお願いしないで済むのもとても良い ◦ ヘルスチェックを実装しなくて良いのでActuatorが便利だった。 
 25

  26. フレームワーク
 • Javaの性能監視にはJolokiaを使用
 ◦ jmxのエンドポイントの設定が不要 ◦ APIでJSONを取得するスタイルなので汎用性が高い • Spring Actuatorが対応しているので、機能を有効にしてjolokia-coreを依存関

    係に追加するだけで使用可能で便利だった。
 
 
 
 • デフォルトでは組み込みTomcatのMbeanが取得できないので下記設定を追 加しました
 26
 # Jolokiaでメトリクス取得するため、TomcatのMbeanを取得可能にする server.tomcat.mbeanregistry.enabled: true # healthとjolokiaを有効に management.endpoints.web.exposure.include: "health,jolokia"
  27. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 27

  28. APIクライアント
 • SpringのRestTemplateを使おうと思ったが、調べてみると今後はメンテナンスモードに なるということだったので比較検討しました
 ◦ 真っ先に上がる候補はSpringのWebClientですが、このためだけにWebFluxを入 れたくないので不採用とした ◦ 結局、OKHttp(Retrofit)を採用した 


    • OkHttp, Retrofitとは
 ◦ OkHttpはHTTP通信に特化した軽量なライブラリでAndroidでよく使われているみた いです。 ◦ RetrofitはOkHttpをラップしてより便利に使えるようにしたライブラリです。 ◦ 去年のアドベントカレンダーで3本記事を書きました 28
 Retrofit+OkHttpでAPIエラーの時のレスポンスの取得ではまった Retrofit+OkHttpでmultipart/form-dataなAPIの呼び出してハマった Retrofit+OkHttpでファイルダウンロードするAPIを呼び出す
  29. APIクライアント
 29
 interface UsersApiService { @GET("{tenantId}/users" ) Call<ResponseData> userList(@Path("tenantId") int

    tenantId, @Query("sort") String sort); } // Responseを詰めるクラスも作っておきます record ResponseData(String status, Integer code, List<User> userList){}; record User(String name, String mailAddress){}; interfaceにAPIごとにメソッドを宣言します。 
 上記の例はGETで {tenantId}/users?sort={sort} を呼び出してレスポンスボディをResponseData型に詰めるという定義です 
 Retrofit retorofit = new Retrofit.Builder() .baseUrl("http://localhost:8080" ) // JSONを変換するコンバーターを指定(今回は Jacksonを使用) .addConverterFactory(JacksonConverterFactory.create()) .build(); // 先ほど作成したinterfaceからインスタンスを生成 UsersApiService service = retorofit.create(UsersApiService. class); // API実行! ResponseData data = service.userList(1, "asc").execute().body(); あとは、下記のように呼び出します。UserApiServiceのメソッド呼び出しは普通のJavaのクラスを呼ぶような感覚で呼び 出すことができます。 • Hello World

  30. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 30

  31. マルチテナント構成
 マルチテナント構成でサービスを提供しています
 ▪マルチテナント構成とは?
 サーバーを複数テナントで共用する構成
 • コストメリット
 • スケーラビリティ
 
 31


    https://jesusgilhernandez.com/2012/12/13/multitenancy-architecture/
  32.  低                                                 マルチテナント構成
 高
 32
  高                                                 A. データベース分離方式 B. 単一データベース・個別スキーマ方式 C.

    単一データベース・共通スキーマ方式 初期構築コスト
 運用コスト
 —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- —----- -------- マルチテナント構成の実現方法
 低

  33. マルチテナント構成
 単一データベース・共通スキーマ方式を採用
 • これまでは単一データベース・個別スキーマ方式を採用していたがお客さんが増えるに従っ てスキーマ数が増えることで管理が大変という問題がある
 • 今回はユーザ特性的にもこの方式が向いていたので無理なく採用
 33
 テナントID 請求書ID

    金額 1 100 1,000,000 2 101 2,000,000 基本すべてのテーブルにテナントIDを持ちます 

  34. マルチテナント構成
 34
 CREATE POLICY isolation_policy ON [table_name] USING (tenant_id =

    current_setting('app.current_tenant_id')::bigint); • PostgreSQLのRow Level Security(RLS)で他テナントのレコードを間違って検索しないことを担 保
 ◦ 万が一SQLでtenant_idでの絞り込みを間違っても、app.current_tenant_idに指定した以外 のデータは取得できないことが担保できる
  35. マルチテナント構成
 35
 • app.current_tenant_idの設定はトランザクションごとに設定するようにspringの DataSourceTransactionManagerを拡張
 public class MyTransactionManager extends DataSourceTransactionManager

    { @Override protected void prepareTransactionalConnection (Connection con, TransactionDefinition definition) throws SQLException { super.prepareTransactionalConnection(con, definition); try (Statement stmt = con.createStatement()) { stmt.execute("SET local app.current_tenant_id = " + TenantContextHolder .getTenantId()); } } } • ここまで仕組みを作ったのでこれで安心できた
 • RLSはSaaSの業界で最近流行っているようで沢山事例が公開されていて参考になりました

  36. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 36

  37. セキュリティ
 • 認証やCSRFチェックはSpring Securityを使用
 ◦ デフォルトで必須級のセキュリティ系のヘッダーを付与してくれるので楽だった 37
 Cache-Control: no-store, no-cache,

    must-revalidate, max-age=0 Pragma: no-cache Expires: 0 X-XSS-Protection: 1; mode=block X-Frame-Options: DENY X-Content-Type-Options: nosniff Strict-Transport-Security: max-age=31536000; includeSubDomains
  38. セキュリティ
 38
 不足するヘッダーだけ自分で設定した Content-Security-Policyはユーザが沢山いる状態で入れるのは大変なので早めに入れられて良かった Referrer-Policy "strict-origin-when-cross-origin" Content-Security-Policy "default-src 'self'; connect-src

    'self' https://www.google-analytics.com; img-src 'self' https://www.google-analytics.com; script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/; font-src https://fonts.googleapis.com/ https://fonts.gstatic.com/; child-src 'none'; object-src 'none'" 「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生 まれる原理と対策の実践」(徳丸本)は必読ですが、 ちょっと前に出た「Webブラウザセキュリティ」という本もとても参考になり ました。 Webブラウザセキュリティ ― Webアプリケーションの安全性を支える仕組みを整理する 米内貴志 著

  39. セキュリティ
 • 認証処理を組み込んだところ性能問題が発生
 ◦ 計測したらログインに2秒近く掛かっていた。 ◦ プロファイルを取ってみるとBCryptPasswordEncoderが遅いことが分かった ◦ 社内のセキュリティ要件に従うためstrengthに14(16384回ストレッチ)を指定したのが原因 ▪

    この件をQiitaにまとめて下さっている人がいました • JavaでBCrypt + ストレッチ回数ごとの処理時間計測 • デフォルトの1024回ストレッチでも100ms以上掛かっているのでそもそもコストが高い処理 ◦ BCryptPasswordEncoderを使用せずに自前実装に切り替えたところ1500msから350msまで改善 しました ▪ なんでこんなに違いが出るのかは細かいことは分かっていません。 39

  40. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 40

  41. タイムゾーン対応
 • ファーストリリースでは日本国内での使用のみ想定すれば良いのだが、将来 的に海外対応が必要になる確度が高いので最初からTimezoneありきで実装を 行った。
 ◦ LocalDatetimeで実装して後から海外対応しようとすると修正箇所が多すぎて辛い ◦ ただし、夏時間を考慮しようとすると途端に難しくなるのでここでは考慮しない 41


    APサーバ ユーザの入力値 :2022-05-10 10:00:00 2022-05-10 T01:00:00+00 ZonedDateTime: 2022-05-10 10:00:00(Asia/Tokyo) OffsetDateTime: 2022-05-10 01:00:00+00 DB
 とりあえずAsia/Tokyo固定 
 でZonedDateTimeで受け取る 
 OffsetDateTime(UTC)に変換して 処理する
 DBにはUTCで格納 
 ※データの取得は逆の順番で同じように行う 

  42. タイムゾーン対応
 42
 • PostgreSQLの timestamp with time zone型について
 timestamp with

    time zoneについて内部に格納されている値は常に UTCです(協定世界時、歴史的にグリニッジ標準時 GMTとして知られています)。 時間帯が明示的に指定された入力値は、その時間帯に適したオフセットを使用して UTCに変 換されます。 入力文字列に時間帯が指定されていない場合は、システムの TimeZoneパラメータに示されている値が時間 帯とみなされ、timezone時間帯用のオフセットを使用して UTCに変換されます。 (公式ドキュメントより引用) 初めて使いましたが、下記の点がちょっと意外でした
 • 時間帯を明示しないと実行環境のTimezoneへの変換が自動で行われる
 • 名前に反してtimezone情報は保持しない。常にUTCに変換して保存される
 ◦ 確かにtimezoneあり/なし、どちらも格納サイズは8バイトで違いはない
  43. タイムゾーン対応
 43
 app=# set timezone to 'Asia/Tokyo'; タイムゾーンをAsia/Tokyoにセットする(大抵の場合デフォルトこれ) SET app=#

    insert into test values(2, '2022-05-09 16:47:29'); 日本時間(+0900)の2022-05-09 16:47:29としてインサート INSERT 0 1 app=# select * from test; id | start_time ----+------------------------ 2 | 2022-05-09 16:47:29+09 日本時間(+0900)で取得される。 (1 row) • SQLクラインアントから実行すると、自動的に日本時間に変換してくれるの でデバッグや運用でのDB操作は問題なさそう

  44. タイムゾーン対応
 ◦ JavaからDBアクセス時に実行環境のtimezoneの影響を受けないようにJVMパラメータで UTC固定にした 44
 -Duser.timezone=UTC ◦ 開発当初はuser.timezoneを指定せず、更にDB Unitのテストデータにもオフセットを指定して いなかったため日本時間としてロードされてしまい日本時間なのかUTCなのか訳が分から

    ない状態になり現場を混乱させてしまった ◦ オフショア開発する可能性もあったのでテストデータにはオフセットを明記するルールにして おいた方が良かったと後から思いました
  45. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 45

  46. フロントエンド
 • 既存サービスを踏襲して、今回のサービスでもReactを採用
 ◦ サーバー側はJSONを返却するAPIを実装する ◦ I/Fだけ固めてしまってフロント側は基本的にお任せ ◦ サーバ側だけでも覚えることが多くなってきており責任分界点ができて良い 


    • フロントエンドについてはJJUG CCC 2021 Springで同僚が話をしていますの で良ければご覧ください
 ◦ 「フロントエンド・バックエンド分離の道のり」 ◦ https://fortee.jp/jjug-ccc-2021-spring/proposal/a0cc4346-f001-44dc-8df4-5974ce40b767 ◦ https://www.youtube.com/watch?v=1b3riDczwkQ 
 46

  47. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 47

  48. クラス設計
 Springのstereotypeを使って素直に実装すると3層アーキテクチャになる
 
 48
 @Controller @Service @Repository ビュー: JSPなど プレゼンテーション層(UI層):


     表示、入力内容受け取り、入力内容のチェック、
  UIに合わせた形式変換
 ビジネスロジック層(ドメイン層):
  業務処理を実装する
 データアクセス層(インフラ層):
  データベースとやりとりを実装する

  49. クラス設計
 • 3層アーキテクチャのメリット
 ◦ いわゆる「トランザクションスクリプト」に処理をつらつら書いて行けばやりたいことが実現できる(オブ ジェクト指向を分かってなくていい) ◦ クラス設計にコストが掛からないので高速に開発できる ◦ ユースケース単位で分担して実装すれば良いので平行して作業しやすい

    
 • 3層アーキテクチャのデメリット
 ◦ 処理の共通化はその人任せになり同じ処理が重複して書かれがちになる ◦ 修正の影響がどこに出るのか探しにくい ◦ 影響があちこちにでてしまう 
 デメリットは既存サービスで味わってきた。
 長期メンテナンスには辛い構造なので時間がなくても今回のサービスでは採用しない
 49

  50. クラス設計
 デメリットはビジネスロジック層に集中しているのでテコ入れする
 50
 @Controller @Service @Repository ビュー: JSPなど プレゼンテーション層(UI層):
  表示、入力内容受け取り、入力内容のチェック、


     UIに合わせた形式変換
 ビジネスロジック層(ドメイン層):
  業務処理を実装する
 データアクセス層(インフラ層):
  データベースとやりとりを実装する
 この層を細分化す るルールを作成す れば良さそう🤔
  51. クラス設計
 オニオンアーキテクチャを採用した
 51
 アプリケーション層 ドメイン層 インフラストラクチャ層 ユーザインターフェース層 アプリケーション層 ドメイン層 インフラストラクチャ層

    ユーザインターフェース層 レイヤードアーキテクチャ
 オニオンアーキテクチャ
 3層アーキ テクチャのビ ジネスロジッ ク層が分離 された
 ドメイン層
 アプリケーション層 
 ユーザインターフェース層 
 インフラストラクチャ層 
 丸で表現するとタマネギみたい な形になるのが由来
 この依存度 の向きが逆
 他にもヘキサゴナルアーキテクチャやクリーンアーキテクチャがあるがそこまで複雑なものは必要なくオニオンアーキテクチャがちょうど 良かった

  52. クラス設計
 • ユースケースをアプリケーション層に、業務ロジックをドメイン層に書くように分 離できた
 ◦ ドメイン層をエンティティやバリューオブジェクトなど、共通認識を持って実装できるのが 大きい ◦ とりあえず小さく始めるならバリューオブジェクトに切り出すことをお勧め 52


  53. クラス設計
 53
 • とは言え、クラスを設計すること自体に慣れてなく苦戦は必至だった
 ◦ だいぶ前から読書会や勉強会を主催、良記事の紹介など地道な啓蒙活動を行った ◦ 既存サービスで部分的に実践 ◦ 理解を深めるため何冊も本を読みました

    エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) エリック・エヴァンス著
 ドメイン駆動設計入門 ボトムアップでわかる! ドメイン駆動設計の基本 成瀬 允宣 著
 ドメイン駆動設計 モデリング/実装ガイド 松岡幸一郎 著
 ドメイン駆動設計 サンプルコード&FAQ 松岡幸一郎 著
 セキュア・バイ・デザイン Dan Bergh Johnsson, Daniel Deogun, Daniel Sawano 著

  54. • はじめに
 • 開発体制
 • Javaのバージョン
 • フレームワーク
 • APIクライアント


    • マルチテナント構成
 • セキュリティ
 • タイムゾーン対応
 • フロントエンド
 • クラス設計
 • ログ
 • おわりに
 54

  55. ログ
 • 運用時の調査のためリクエスト/レスポンス情報をログ出力した い
 • ユーザの入力値など機微情報は除外してデータを特定するID項 目だけをログに出力したい
 • Springのフィルターは全てログに出力してしまうため使えない
 •

    機微情報を意識しながらControllerでログ出力実装をしたくない
 →ログ出力をするFilterを実装すれば簡単にできそう🤔
 55

  56. ログ
 • Responseからデータを取得しログ出力できたが後続の処理でス トリームが閉じられていると言ってエラーになってしまう
 56
 やってみたら意外と難しかった。


  57. ログ
 • AbstractRequestLoggingFilterを参考にContentCachingRequestWrapper、 ContentCachingResponseWrapperを使用するように実装しました
 57
 @AllArgsConstructor public class RequestAndResponseLoggingFilter extends

    OncePerRequestFilter { @Override protected void doFilterInternal (@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) { doFilterWrapped(wrapRequest(request), wrapResponse(response), filterChain); } private void doFilterWrapped (ContentCachingResponseWrapper request, ContentCachingResponseWrapper response) { try { // リクエストログ出力 (予め指定してキーに絞ってログ出力) writeRequestLog(request); // 処理の実行 long start = System.currentTimeMillis(); filterChain.doFilter(request, response); long latency = System.currentTimeMillis() - start; // レスポンスログ出力 (予め指定してキーに絞ってログ出力) writeResponseLog(response , latency); } finally { response.copyBodyToResponse(); // レスポンスに書き込み } } private ContentCachingResponseWrapper wrapRequest(HttpServletRequest request) { return new ContentCachingResponseWrapper (request); } private ContentCachingResponseWrapper wrapResponse(HttpServletResponse response) { if (response instanceof ContentCachingResponseWrapper wrapper) { return wrapper; } else { return new ContentCachingResponseWrapper(response); } } }  ※メモリ使用量は増えると思いますので、ご注意を。

  58. 他にも沢山のタスクをこなす必要がある...
 • パブリッククラウド or オンプレ検討
 • コンテナ or VM or

    物理サーバ
 • システム構成検討
 • スケールアウト方式検討(APP/DB)
 • バックアップ・リストア検討
 • ドメイン取得
 • URL設計
 • SSL証明書取得(暗号化方式, TLSバージョン)
 • ログ設計(syslogの設計)
 • CI設定
 • systemd設定
 • サーバのディレクトリ構成設計
 • オブジェクトストレージ設計
 • 各種タイムアウト値設計
 • リクエストサイズの上限設定 (servlet.multipart.maxFileSize, servlet.multipart.maxRequestSize)
 • Cookie設計
 • セッション設計
 • LBの設定(Sticky Session)
 • PostgreSQLの設定値検討
 • PostgreSQLのスキーマ設計
 • PostgreSQLのRole設計
 • コネクションプーリング検討
 58
 • JVMパラメータ設計(ヒープサイズ、Metaspaceなど)
 • GC設計(GC種類の検討、GCログなど)
 • 性能測定
 • パッケージ設計
 • 使用するMWのライセンス確認
 • 排他制御の方式検討
 • ウィルススキャン検討
 • コーディング・DB規約作成
 • バッチ設計
 • 制御文字チェック(nullやnbspなどバイト列)
 • メール設計(spf、dkim、smtp、メールヘッダーの検討など)
 • メールテンプレート設計(テンプレートエンジンは何を使うかなど)
 • TomcatやSpringのエラー画面を表示させない対応
 • ステージング設計
 • ヘルスチェック設計
 • APM検討
 • 文字コード検討
 • クローラー対策
 • 脆弱性チェック
 • 運用設計(スレッドダンプの取得方法の検討など)
 • メッセージ設計(他言語対応しやすいようにベタ書き禁止)
 • ORマッパーの検討
 • Reactのためのmod_rewrite設定

  59. 🎊予定通り無事ローンチできた🎉
 
 
 (電子帳簿保存法に二年間の猶予期間が設けられたのはまた別の話😂) 
 59


  60. おわりに
 • すべてが理想通りとは行かなかったが納期を守ってリリースできた
 • 久しぶりに新サービスを開発をしてみて、1つのサービスを作るために必 要なことは沢山あることを改めて実感した
 • 新しい技術要素を試すためには日頃からの仕込みが重要
 • 半年サイクルのリリースになりJavaはリリース直後でも安心して利用でき

    た
 60

  61. 🏁ご清聴ありがとうございました🏁
 61