136
CyberAgent, Inc. All Rights Reserved アメブロの大規模システム刷新と それを支えるSpring 1

アメブロの大規模システム刷新と それを支えるSpring

Embed Size (px)

Citation preview

Page 1: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

アメブロの大規模システム刷新とそれを支えるSpring

1

Page 2: アメブロの大規模システム刷新と それを支えるSpring

自己紹介

向井 政貴

株式会社サイバーエージェント

サーバサイドエンジニア

アメーバブログの開発・運用を担当

最近会社の料理部が楽しい

2

Page 3: アメブロの大規模システム刷新と それを支えるSpring

2015年の状況

システム刷新

まとめ

アメーバブログとは

3

Page 4: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

アメーバブログ

4

Page 5: アメブロの大規模システム刷新と それを支えるSpring

アメーバブログ

PC、スマホ、ガラケー、アプリ

ランキングなど周辺サービス

5

Page 6: アメブロの大規模システム刷新と それを支えるSpring

アメーバブログ

2004年9月サービス開始

約12年続くサービス

6

Page 7: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

2015年アメブロの状況

7

Page 8: アメブロの大規模システム刷新と それを支えるSpring

古いバージョンの技術

8

Page 9: アメブロの大規模システム刷新と それを支えるSpring

MySQL 4系

CentOS 4系

java 1.5 ~ 1.7

9

Page 10: アメブロの大規模システム刷新と それを支えるSpring

複数のプロダクトに

似たような処理

10

Page 11: アメブロの大規模システム刷新と それを支えるSpring

ブラウザから記事投稿できるよねアプリからも記事をかけるようにしたい!

同じ機能だけどAPIとして新しく作るか・・

アメブロ

記事投稿

API

記事投稿

11

Page 12: アメブロの大規模システム刷新と それを支えるSpring

API

ブラウザから読者登録できるよねアプリからも読者登録したい!

アメブロ

記事投稿

API

記事投稿

読者登録 読者登録

同じ機能だけどAPIとして新しく作るか・・

12

Page 13: アメブロの大規模システム刷新と それを支えるSpring

API API

調査用に記事情報を取得したい!

アメブロ

記事投稿

API

記事投稿

読者登録 読者登録

同じ機能だけどAPIとして(ry

記事取得 記事取得

13

Page 14: アメブロの大規模システム刷新と それを支えるSpring

フレームワークの混在

14

Page 15: アメブロの大規模システム刷新と それを支えるSpring

Struts 1.x , S2Struts, Spring 3.x

● Struts から Spring3 に移行しようと頑張った

形跡はある…

● ややこしい

● 学習コスト

15

Page 16: アメブロの大規模システム刷新と それを支えるSpring

苦労の多い動作確認環境

16

Page 17: アメブロの大規模システム刷新と それを支えるSpring

動作確認方法の歴史

2013年

.class

SCP!!!!

ビルド

.class

チーム共用ステージングサーバ

17

Page 18: アメブロの大規模システム刷新と それを支えるSpring

動作確認方法の歴史

2013年

2014年 個人開発用サーバ

PUSH

DEPLOY

18

Page 19: アメブロの大規模システム刷新と それを支えるSpring

動作確認方法の歴史

2013年

2014年 個人開発用サーバ

2015年 ココ

19

Page 20: アメブロの大規模システム刷新と それを支えるSpring

失われた思想(?)

20

Page 21: アメブロの大規模システム刷新と それを支えるSpring

実際のコード例:記事データ検索メソッド

public void search(boolean showEntryTextFlag) {

!? !?

21

Page 22: アメブロの大規模システム刷新と それを支えるSpring

いろいろあるけど大まかに言うと

22

Page 23: アメブロの大規模システム刷新と それを支えるSpring

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題

23

Page 24: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

さらに

24

Page 25: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved_人人人人人人人人人人人_>  データセンター移設  <

 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄

25

Page 26: アメブロの大規模システム刷新と それを支えるSpring

アメブロのシステムの大部分が

稼働していたDCから撤退

26

Page 27: アメブロの大規模システム刷新と それを支えるSpring

保守機材の在庫が限界

27

Page 28: アメブロの大規模システム刷新と それを支えるSpring

LBやコアスイッチの調子が悪化

28

Page 29: アメブロの大規模システム刷新と それを支えるSpring

1. 既存システムのまま移設する

2. 作り直して移設する

選択肢

29

Page 30: アメブロの大規模システム刷新と それを支えるSpring

どちらにするか

30

Page 31: アメブロの大規模システム刷新と それを支えるSpring

作り直すとして間に合うのか

31

Page 32: アメブロの大規模システム刷新と それを支えるSpring

何か障害が起きないか

32

Page 33: アメブロの大規模システム刷新と それを支えるSpring

怖い

33

Page 34: アメブロの大規模システム刷新と それを支えるSpring

刷新前のシステムつらい

34

Page 35: アメブロの大規模システム刷新と それを支えるSpring

面白くない

35

Page 36: アメブロの大規模システム刷新と それを支えるSpring

このままだと新しい人が入ってこない

36

Page 37: アメブロの大規模システム刷新と それを支えるSpring

世間の技術トレンドから遅れる

37

Page 38: アメブロの大規模システム刷新と それを支えるSpring

開発速度は遅くなる一方

38

Page 39: アメブロの大規模システム刷新と それを支えるSpring

39

Page 40: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

システム刷新だ

40

Page 41: アメブロの大規模システム刷新と それを支えるSpring

システム規模

41

40超

数百台

刷新後のあるAPIサーバ

へのリクエスト数 60万リクエスト/m

サーバ台数

プロジェクト数

5300万人(2016年8月時点)会員数

Page 42: アメブロの大規模システム刷新と それを支えるSpring

ページがみれないと広告が表示されないため売上に直撃

社内他サービスへの影響

芸能事務所から問い合わせ

システムになにかあると...

42

Page 43: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

システム刷新の対象

43

Page 44: アメブロの大規模システム刷新と それを支えるSpring

44

フロントエンド

バックエンド

Page 45: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

システム刷新の要件

45

Page 46: アメブロの大規模システム刷新と それを支えるSpring

障害なし

長期のサービス停止は回避

DC移設までに完遂

画面や機能の仕様を維持

開発コスト減少

一般的なアーキテクチャ

MUST WANT

46

Page 47: アメブロの大規模システム刷新と それを支えるSpring

47

Page 48: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

Why Spring?

48

Page 49: アメブロの大規模システム刷新と それを支えるSpring

低い導入コスト

開発陣の慣れ

テンプレートの使い回し

刷新前のシステムにSpringの採用実績

49

Page 50: アメブロの大規模システム刷新と それを支えるSpring

ローカル開発環境

開発コストの削減

ローカル開発による生産性の向上

50

Page 51: アメブロの大規模システム刷新と それを支えるSpring

@Controller, @Service, @Repository

共通の認識があるレイヤードなアーキテクチャ

アーキテクチャを統一しやすい

51

Page 52: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

システム刷新

52

Page 53: アメブロの大規模システム刷新と それを支えるSpring

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題(再掲)

53

Page 54: アメブロの大規模システム刷新と それを支えるSpring

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

従来システムの課題

54

Page 55: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

保守性の低下

55

Page 56: アメブロの大規模システム刷新と それを支えるSpring

処理が適切な粒度でまとまっていない

56

刷新前システムの課題

Page 57: アメブロの大規模システム刷新と それを支えるSpring

アメブロ

ORM

ビジネスロジック

57

人によって処理を書く場所がバラバラ

再利用性や可読性が低い

刷新前のシステム

Page 58: アメブロの大規模システム刷新と それを支えるSpring

レイヤードアーキテクチャ

58

Page 59: アメブロの大規模システム刷新と それを支えるSpring

刷新後システムの設計

Controller

Facade

Service

Repository

リクエストを受けて結果を返す

一操作に必要なServiceの呼び出し

最低限、まとめておくべきビジネスロジック

DBアクセスやAPIの呼び出し

59

ポリシーの統一化

再利用性、可読性向上

Page 60: アメブロの大規模システム刷新と それを支えるSpring

メンテされているのかわからない

APIドキュメント

刷新前システムの課題

60

Page 61: アメブロの大規模システム刷新と それを支えるSpring

wikiにAPIドキュメント用意

手動でメンテナンス

実際のソースコードとの整合性が不安

61

Page 62: アメブロの大規模システム刷新と それを支えるSpring

Springfox + Swaggerによる

自動ドキュメント化

62

Page 63: アメブロの大規模システム刷新と それを支えるSpring

https://github.com/springfox/springfox

https://github.com/swagger-api/swagger-ui

63

Page 64: アメブロの大規模システム刷新と それを支えるSpring

@RequestMapping( method = RequestMethod.GET, value = "/v1.0/public/blogger/{amebaId}" ) public BloggerResponse getBlogger ( @PathVariable("amebaId") String amebaId ) { return bloggerFacade.getBlogger(amebaId); }

64

Page 65: アメブロの大規模システム刷新と それを支えるSpring

エンドポイントを自動的にドキュメントに反映

複雑なドキュメントはSwaggerのアノテーションが必要

エンドポイントのきれいな一覧だけでもだいぶありがたい

65

Page 66: アメブロの大規模システム刷新と それを支えるSpring

ここまでで25分ぐらい

66

Page 67: アメブロの大規模システム刷新と それを支えるSpring

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題

67

Page 68: アメブロの大規模システム刷新と それを支えるSpring

自己紹介

服部 拓也

2013年 株式会社サイバーエージェント入社

入社以来アメーバブログのサーバーサイドを担当

現在はディレクションとマネジメントを兼務

マイブームは喘息

68

Page 69: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

69

 開発コスト

Page 70: アメブロの大規模システム刷新と それを支えるSpring

70

ローカル開発環境ない ・ 

Page 71: アメブロの大規模システム刷新と それを支えるSpring

動作確認方法の歴史 (再掲)

2013年

2014年 個人開発用サーバ

2015年 ココ

71

Page 72: アメブロの大規模システム刷新と それを支えるSpring

72

Page 73: アメブロの大規模システム刷新と それを支えるSpring

73

ローカル開発環境構築が容易

フロントエンドエンジニアでも簡単

-> 開発速度UP

Page 74: アメブロの大規模システム刷新と それを支えるSpring

74

プロダクトごとに

リポジトリが分散

Page 75: アメブロの大規模システム刷新と それを支えるSpring

75

Page 76: アメブロの大規模システム刷新と それを支えるSpring

76

Page 77: アメブロの大規模システム刷新と それを支えるSpring

77

ソースコードもリリースジョブも

再利用性が低い

削減できるはずのコスト

Page 78: アメブロの大規模システム刷新と それを支えるSpring

78

リポジトリの統合 ・

Page 79: アメブロの大規模システム刷新と それを支えるSpring

79

Page 80: アメブロの大規模システム刷新と それを支えるSpring

マルチプロジェクト化

80

Page 81: アメブロの大規模システム刷新と それを支えるSpring

81

リリースジョブを共通化できる

処理の見通しがよくなる

処理を共通化しやすくなる

Page 82: アメブロの大規模システム刷新と それを支えるSpring

刷新後システムの処理共通化

API βAPI α

Controller

Facade

Service

Repository

Controller

Facade・・・

82

Page 83: アメブロの大規模システム刷新と それを支えるSpring

マルチプロジェクト構成によるコードの共通化

リリースジョブの統一化

-> 開発効率UP

83

Page 84: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

だがしかし…

84

Page 85: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによって

不要なのにBean化されてしまうクラスが増大

85

Page 86: アメブロの大規模システム刷新と それを支えるSpring

86

起動のためだけに

不要なBeanの設定が必要

Page 87: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

リポジトリ

Project A

Project B

Service Project Repository Project

87

Page 88: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

Project A

Project B

88

リポジトリ

Page 89: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

Project A

89

Page 90: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

Project A

90

Page 91: アメブロの大規模システム刷新と それを支えるSpring

どうしたか

まずは Service

91

Page 92: アメブロの大規模システム刷新と それを支えるSpring

FacadeクラスがAutowiredしているServiceクラスだけを

Bean化しよう!

● @Facadeアノテーションを作った

● @FacadeのついたクラスをBean化して、

そのFacadeクラスがAutowiredしているServiceクラスだけを

Bean化するInitializerを作った

92

Page 93: アメブロの大規模システム刷新と それを支えるSpring

@ComponentScan( excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Facade.class ), @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Service.class ) })

適用例

public class Main { public static void main(String[] args) {  new SpringApplicationBuilder(Main.class)   .initializers(    new FacadeInitializer()   )   .run(args); }}

93

Page 94: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

@Facade

@Service

@Service

repository D

@Service

@Service

Project A

94

Page 95: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

@Facade

@Service

@Service

repository D

@Service

@Service

95

Project A

Page 96: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

@Facade

@Service

@Service

repository D

@Service

@Service

96

Project A

Page 97: アメブロの大規模システム刷新と それを支えるSpring

どうしたか

Repository編

97

Page 98: アメブロの大規模システム刷新と それを支えるSpring

プロダクト毎に接続先DBの情報をapplication.ymlに宣言する

● 宣言した接続先に関連するRepositoryだけをBean化する

Initializerを作った

98

Page 99: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

A repository B repository

C Datasource

C repositoryA repository B repository

C repository

application.ymlInitializer

99

Page 100: アメブロの大規模システム刷新と それを支えるSpring

100

jdbc: blog: // [HOST] + [SCHEME] の組み合わせにユニークなネームスペースをつけたイメージ

master: url: jdbc:mysql://[HOST]/[SCHEME]?zeroDateTimeBehavior=convertToNull&... initialSize: 1 maxTotal: 1 ... slave: url: jdbc:mysql://[HOST]/[SCHEME]?zeroDateTimeBehavior=convertToNull&... initialSize: 1 maxTotal: 1 ... entry: slave: url: jdbc:mysql://[HOST]/[SCHEME]?zeroDateTimeBehavior=convertToNull&... initialSize: 1 maxTotal: 1 ...

application.yml

Page 101: アメブロの大規模システム刷新と それを支えるSpring

@ComponentScan( excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Repository.class ), @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Facade.class ), @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Service.class ) })

public class Main { public static void main(String[] args) {  new SpringApplicationBuilder(Main.class)   .initializers(    new RepositoryInitializer(),    new FacadeInitializer()   )   .run(args); }}

101

適用例

Page 102: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

102

Page 103: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

103

Page 104: アメブロの大規模システム刷新と それを支えるSpring

外部API接続用のRepositoryが残ってる

104

Page 105: アメブロの大規模システム刷新と それを支えるSpring

RepositoryクラスのBean化(API接続用)

@Configurationpublic class SampleConfiguration { @Bean public SampleApiClient SampleApiClient(

HttpClient httpClient,@Value("${sample.server.scheme}") String scheme,@Value("${sample.server.host}") String host,@Value("${sample.server.port}") Integer port

) {・・・

}

@Bean public SampleRepository SampleRepository (SampleApiClient sampleApiClient) {・・・}}

# application.yml

sample:scheme: httphost: sample.ameba.jpport: 80

105

Page 106: アメブロの大規模システム刷新と それを支えるSpring

RepositoryクラスのBean化(API接続用)@ConditionalOnProperty (

prefix = "sample", name = "enabled",havingValue = "true",matchIfMissing = false

)@Configurationpublic class SampleConfiguration { @Bean public SampleApiClient SampleApiClient(

HttpClient httpClient,@Value("${sample.server.scheme}") String scheme,@Value("${sample.server.host}") String host,@Value("${sample.server.port}") Integer port

) {・・・

}

@Bean public SampleRepository SampleRepository (SampleApiClient sampleApiClient) {・・・}}

# application.yml

sample:scheme: httphost: sample.ameba.jpport: 80enabled: true

106

Page 107: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

107

Page 108: アメブロの大規模システム刷新と それを支えるSpring

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

108

Page 109: アメブロの大規模システム刷新と それを支えるSpring

共通化して起こった問題もSpringの機能で解決

109

Page 110: アメブロの大規模システム刷新と それを支えるSpring

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題

110

Page 111: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

 ビューとビジネスロジックの 強い依存関係

111

Page 112: アメブロの大規模システム刷新と それを支えるSpring

Data Store

刷新前のシステム

アメブロ

ビジネスロジック

ORM

他サービスAPI

他サービスのAPI

112

ビュー

Page 113: アメブロの大規模システム刷新と それを支えるSpring

刷新前のチームとシステムの関わり方

113

Page 114: アメブロの大規模システム刷新と それを支えるSpring

サーバーサイドフロントエンド

刷新前のチームとシステムの関わり方

サーバーとフロントでチームが別れている

ただし同じシステムをさわる

フロントエンドの最新技術導入しづらい

アメブロ

ORM

ビジネスロジック

ビュー

114

Page 115: アメブロの大規模システム刷新と それを支えるSpring

刷新前の他部署との連携状況

115

Page 116: アメブロの大規模システム刷新と それを支えるSpring

刷新前の他部署との連携状況アメブロ

ビジネスロジック

ORM

秋葉原ラボ

ティーン向けブログサービス

etc..

記事データ取りたい

アメブロにも投稿したい

この中に組み込まれている機能を使いたい

116

ビュー

◯◯API

ORM

新規作成

ビジネスロジック

Page 117: アメブロの大規模システム刷新と それを支えるSpring

API Centricな

アーキテクチャ ・

117

Page 118: アメブロの大規模システム刷新と それを支えるSpring

Data Store

刷新前のシステム(再掲)

アメブロ

ビジネスロジック

ORM

他サービスAPI

他サービスのAPI

118

ビュー

Page 119: アメブロの大規模システム刷新と それを支えるSpring

刷新後のシステム

PC アメブロ

Controller

API

Facade

Service

スマホ アメブロ

Controller

Facade

Service

Controller

Facade

Service

Repository

ビューを生成する処理に集中

ビジネスロジックの提供に集中

Data Store

他サービスのAPI

119

JSON

ビュー ビュー

Page 120: アメブロの大規模システム刷新と それを支えるSpring

すぐに良いことが

120

Page 121: アメブロの大規模システム刷新と それを支えるSpring

他部署からの依頼対応工数の削減

AMPを特別対応なしで完了

スマホ版フロントエンドの刷新も特別対応なし

  参考) アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~

https://developers.cyberagent.co.jp/blog/archives/636/

121

Page 122: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

API Centricの課題

122

Page 123: アメブロの大規模システム刷新と それを支えるSpring

通信コスト

123

Page 124: アメブロの大規模システム刷新と それを支えるSpring

複数レイヤにキャッシュを入れて対応

PC アメブロ

Controller

API

Facade

Service

スマホ アメブロ

Controller

Facade

Service

Controller

Facade

Service

Repository

Data Store

他サービスのAPI

124

ビュー ビュー

Client でキャッシュ

Repositoryでキャッシュ

Page 125: アメブロの大規模システム刷新と それを支えるSpring

APIの呼び出し元・先が

わかりづらい

(静的な意味で)

125

Page 126: アメブロの大規模システム刷新と それを支えるSpring

未解決

126

Page 127: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

まとめ

127

Page 128: アメブロの大規模システム刷新と それを支えるSpring

技術面

ビジネス面

組織面

128

Page 129: アメブロの大規模システム刷新と それを支えるSpring

技術面でよかったこと

129

Page 130: アメブロの大規模システム刷新と それを支えるSpring

フロントエンドの技術がトレンドに乗りやすくなった

参考) アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~

https://developers.cyberagent.co.jp/blog/archives/636/

一般的なアーキテクチャに則った作りにできた

セキュリティリスクも抑制できている

技術的によかったこと

130

Page 131: アメブロの大規模システム刷新と それを支えるSpring

ビジネス面でよかったこと

131

Page 132: アメブロの大規模システム刷新と それを支えるSpring

AMP対応を短期間で完遂

フロントエンドを刷新しPVや広告収益が

短期間でより多くの施策が実現可能に

ビジネス的によかったこと

132

Page 133: アメブロの大規模システム刷新と それを支えるSpring

組織面でよかったこと

133

Page 134: アメブロの大規模システム刷新と それを支えるSpring

フロントエンドの開発チームを分離できた

新規の人材を取り込みやすくなった

よりよいサービスにしようという意識が強くなった

組織的によかったこと

134

Page 135: アメブロの大規模システム刷新と それを支えるSpring

CyberAgent, Inc. All Rights Reserved

最後に

135

Page 136: アメブロの大規模システム刷新と それを支えるSpring

本当に

やってよかった

136