$30 off During Our Annual Pro Sale. View Details »

百科事典の責務分割 PIXIV SPRING BOOT CAMP 2022 技術基盤(Webフレームワーク)

usuyuki
March 17, 2022

百科事典の責務分割 PIXIV SPRING BOOT CAMP 2022 技術基盤(Webフレームワーク)

PIXIV SPRING BOOT CAMP 2022での技術基盤(Webフレームワーク)コースに参加しました。
この資料は8日間のインターンを終えて最終発表で用いた資料です!
※公開許可取得済

usuyuki

March 17, 2022
Tweet

More Decks by usuyuki

Other Decks in Technology

Transcript

  1. 百科事典の責務分割
    PIXIV SPRING BOOT CAMP 2022 技術基盤(Webフレームワーク)
    pixiv Inc.
    usuyuki
    2022.3.17

    View Slide

  2. 2
    自己紹介
    ● アイコンの背景色とpixivのカラーが似ている
    ● 鳥取県米子市生、島根県浜田市育ち
    ● コーヒーが好き
    ● しぐれうい先生を推しています
    (イラストレーターとしても、VTuberとしても)
    ● なんちゃってPHPer
    ○ Laravelをよく使います
    ● 心ばかりの
    ○ 自然言語処理
    ○ インフラ環境構築
    ○ JSやPythonなど
    usuyuki
    宇都宮大学

    View Slide

  3. 3
    ピクシブ百科事典の
    改善
    メンターの
    tadsan
    インターンの
    usuyuki
    usuyukiの力上昇
    メンターのtadsan

    View Slide

  4. ‘ピクシブ百科事典はアニメやマンガ、ゲームからデザイン・アートまで
    あらゆる言葉・現象・文化・作品を解説するみんなでつくる百科事典です。

    4
    ピクシブ百科事典
    記事数
    42万
    ※日本語版
    月間PV
    1億
    ※日本語版

    View Slide

  5. 2009年来からのPHP製
    独自フレームワーク
    5
    ピクシブ百科事典

    View Slide

  6. 6
    8日間で行ったこと
    大きく分けると2つ

    View Slide

  7. 7
    8日間で行ったこと
    ※イメージ
    PHP
    フレームワーク
    アプリケーション

    View Slide

  8. 8
    8日間で行ったこと
    ※イメージ
    PHP
    フレームワーク
    アプリケーション
    ルーティングを
    middlewareへ

    View Slide

  9. 9
    8日間で行ったこと
    ※イメージ
    PHP
    フレームワーク
    アプリケーション
    /mypageのADR化

    View Slide

  10. 10
    フレームワークのお話
    ※イメージ
    PHP
    フレームワーク
    アプリケーション

    View Slide

  11. 11
    やったこと
    ルーティングをmiddlewareへ

    View Slide

  12. 12
    全体の流れ
    ページの閲覧
    index.php
    種々のミドルウェア
    Dispatcher
    ControllerまたはAction
    Model
    tplファイル
    ● 田代砲やNGワードを弾く
    ● HTTPSへのリダイレクト
    ● セッションの初期化

    View Slide

  13. 13
    全体の流れ
    ページの閲覧
    index.php
    種々のミドルウェア
    Dispatcher
    ControllerまたはAction
    Model
    tplファイル
    URLを元に対応するクラスやメ
    ソッドを引っ張り出す

    View Slide

  14. 14
    全体の流れ
    ページの閲覧
    index.php
    種々のミドルウェア
    Dispatcher
    ControllerまたはAction
    Model
    tplファイル
    実際の処理
    (記事の登録や呼び出し )

    View Slide

  15. 15
    全体の流れ
    ページの閲覧
    index.php
    種々のミドルウェア
    Dispatcher
    ControllerまたはAction
    Model
    tplファイル

    View Slide

  16. 16
    何が嬉しいの?
    外部依存削減
    &
    責務分割

    View Slide

  17. 17
    外部依存削減
    外部依存削減

    View Slide

  18. 18
    外部依存削減
    その前に

    View Slide

  19. 19
    PSR
    PSR

    View Slide

  20. 20
    PSR
    PHP
    Standard
    Recommendationd
    PSR

    View Slide

  21. 21
    PSR
    ピクシブ百科事典はPSR準拠に

    View Slide

  22. 22
    PSR-7 HTTP Message Interface
    リクエストやレスポンスの
    インターフェイスなどが定義されている!

    View Slide

  23. 23
    PSR-7 HTTP Message Interface
    そしてイミュータブル(不変)!

    View Slide

  24. //どこかのファイル
    $_POST[‘title’] = ”任意の値”;
    PHPのスーパーグローバル変数
    24
    //どこかのファイル
    $title = $_POST[‘title’];//何が来るか分からない...
    値の代入(任意の場所で任意の回数)
    値の取り出し(グローバル変数)

    View Slide

  25. $request = $request->withAttribute(RouteDispatchHandler::class, $new_handler);
    PSR-7準拠
    25
    public function handle(ServerRequestInterface $request)
    {
    $request->getAttribute(RouteDispatchHandler::class, $new_handler);
    値の代入(1回限り)
    値の取り出し(引数より頂戴できる)

    View Slide

  26. 26
    こうすることで...
    これまで
    これから
    session_user()//関数内で$_SESSION['user']が呼ばれる
    public function hanlde(ServerRequestInterface $request)
    {
    //中略
    $request->getAttribute(‘user’)

    View Slide

  27. 27
    外部依存削減
    外部依存削減

    View Slide

  28. 28
    責務分割
    責務分割

    View Slide

  29. 29
    Dispatcher
    これまで
    ● httpならリダイレクト、セッションの初期化、
    ● urlから対応づくControllerやActionのクラスとメソッドを見つける
    ● 問題のあるタイトルや意図しないものを弾く
    ● 該当するクラスのメソッドを呼び出す
    ● 該当するクラスのメソッドを呼び出す
    これから

    View Slide

  30. 30
    PSR-15 HTTP Handlers
    middlewareやhandlerに関しての
    インターフェイス

    View Slide

  31. class RoutingMiddleware implements MiddlewareInterface
    {
    インターフェイスを具象化したものを実装
    31
    class IndexAction implements RequestHandlerInterface
    {
    Mypage/IndexAction
    RoutingMiddleware

    View Slide

  32. 32
    PSR-15
    middleware
    middleware
    HTTPリクエスト HTTPレスポンス

    View Slide

  33. 33
    PSR-15
    middleware
    middleware
    HTTPリクエスト HTTPレスポンス

    View Slide

  34. 34
    責務分割
    middlewareへ切り出した

    View Slide

  35. 35
    従来
    https://dic.pixiv.net/a/%E3%81%97%E3%81%90%E3%82%8C%E3%81%86%E3%81%84
    dispatcher RouteConfigPC
    URLに対してどれを
    呼び出すかが列挙されている
    実際の処理(Controller,Action)
    を呼び出す!
    種々のmiddleware

    View Slide

  36. 36
    今回の実装
    https://dic.pixiv.net/a/%E3%81%97%E3%81%90%E3%82%8C%E3%81%86%E3%81%84
    dispatcher
    RouteConfigPC
    URLに対してどれを
    呼び出すかが列挙されている
    RoutingMiddleware
    実際の処理を呼び出す
    RouteDispatchHandler
    該当する処理を見つけ出し、
    RouteDispatchHandlerを$request
    に包む
    実際の処理を準備する
    種々のmiddleware
    種々のmiddleware

    View Slide

  37. //色々な処理...
    $new_handler = new RouteDispatchHandler($route, $controller,
    $this->response_factory, $this->stream_factory, $this->system_clock,
    $this->views_dir);
    $request = $request->withAttribute(RouteDispatchHandler::class, $new_handler);
    return $handler->handle($request);
    RoutingMiddleware
    37

    View Slide

  38. //色々な処理...
    $handler = $request->getAttribute(RouteDispatchHandler::class);
    assert($handler instanceof RouteDispatchHandler);
    return $handler->handle($request);
    Dispatcher
    38

    View Slide

  39. //色々な処理...
    $handler = $request->getAttribute(RouteDispatchHandler::class);
    assert($handler instanceof RouteDispatchHandler);
    return $handler->handle($request);
    Dispatcher
    39

    View Slide

  40. Dispatcherの責務を減らす
    40
    237行
    80行

    View Slide

  41. 41
    PSR
    middleware
    HTTPリクエスト HTTPレスポンス
    PSR-7
    ServerRequestInterface
    PSR-7
    ResponseInterface
    PSR-15
    RequestHandlerInterface
    PSR-15
    MiddlewareInterface

    View Slide

  42. 42
    テストが書ける
    ● 途中に謎の要素を取り込まない!
    ● 責務が分割されている
    単体で完結してるので
    テストが書ける!

    View Slide

  43. 43
    アプリケーションのお話
    ※イメージ
    PHP
    フレームワーク
    アプリケーション

    View Slide

  44. 44
    やったこと
    /mypageのADR(風味)化

    View Slide

  45. 45
    MVC
    Model
    View
    Controller
    レスポンス
    リクエスト

    View Slide

  46. 46
    ADR
    Domain
    Responder
    Action
    レスポンス
    リクエスト
    HTTPレスポンス作成
    メソッド1つのみ
    (今回はDomain風味)

    View Slide

  47. if (!is_login()) {
    return $this->createRedirectResponse('/login', ['return_to' =>
    (string)$this->request->getUri()]);
    }
    $user = session_user();
    $this->set('user', $user);
    [$checklist, $checklist_count] =
    ChecklistModel::findByUserIDWithArticle($user->user_id, 0, 5);
    //続く...
    47
    旧:UserController mypageメソッド

    View Slide

  48. public function handle(ServerRequestInterface $request): ResponseInterface
    {
    $user = $request->getAttribute('user');
    if ($user instanceof NotLoggedIn) {
    return $this->responder->redirectForLogin((string)$request->getUri());
    }
    //中略
    return $this->responder->emitHtml($checklist, $created_articles, $activity,
    $access, $related_articles);
    新:Mypage/IndexAction handleメソッド
    48

    View Slide

  49. public function emitHtml(array $checklist, array $created_articles, array
    $activity, array $access, array $related_articles): ResponseInterface
    {
    $html = $this->html_factory->render($this->views_dir . '/user/mypage', [
    'checklist' => $checklist,
    //略
    ]);
    return $this->response_factory->createResponse(200)
    ->withBody($this->stream_factory->createStream($html));
    }
    新:Mypage/IndexResponder
    49

    View Slide

  50. 50
    DI
    Mypage/IndexActionでResponderをDI!
    返し方をActionは知らなくて良い
    ※抽象に依存という点では厳密には不十分

    View Slide

  51. /** @var IndexResponder */
    private $responder;
    public function __construct(IndexResponder $responder)
    {
    $this->responder = $responder;
    }
    Mypage/IndexAction のコンストラクタ
    51

    View Slide

  52. public function emitHtml(array $checklist, array $created_articles, array
    $activity, array $access, array $related_articles): ResponseInterface
    {
    $html = $this->html_factory->render($this->views_dir . '/user/mypage', [
    'checklist' => $checklist,
    //略
    ]);
    return $this->response_factory->createResponse(200)
    ->withBody($this->stream_factory->createStream($html));
    }
    新:Mypage/IndexResponder
    52

    View Slide

  53. public function handle(ServerRequestInterface $request): ResponseInterface
    {
    $user = $request->getAttribute('user');
    if ($user instanceof NotLoggedIn) {
    return $this->responder->redirectForLogin((string)$request->getUri());
    }
    //中略
    return $this->responder->emitHtml($checklist, $created_articles, $activity,
    $access, $related_articles);
    Mypage/IndexAction handleメソッド
    53

    View Slide

  54. 54
    まとめ
    ● ルーティング周りのmiddleware移行(PSR準拠)
    ● /mypageのADR(風味)化
    ● これらのテストを作成

    View Slide

  55. 55
    書ききれなかったけど学んだこと……
    ● PHPStanでの静的解析
    ● PHPDocの威力
    ○ ジェネリックス
    ● テストの疎結合
    ● staticMock
    ● autowire
    ● git周りの知見
    ● PSRの仕様に関して
    ● 名前空間
    ● オートロード
    ● PHPの関数呼び出し順による最適化
    ● クリーンとイージーに関して
    ● interface,trait,abstract
    ● 安定度抽象度等価原則に関して
    ● デプロイ環境に関して
    などなど...

    View Slide

  56. 56
    まとめ
    この春
     ピクシブで
      圧倒的猛者になりました。

    View Slide

  57. 57
    謝辞
    tadsanさん、kamikoさん
    pixiv事業本部webエンジニアリングチームの皆様、
    障害対応してくださった皆様、
    インターンの手筈整えてくださった皆様、
    充実した8日間をありがとうございました。

    View Slide

  58. 58
    ピクシブ百科事典の
    改善
    メンターの
    tadsan
    インターンの
    usuyuki
    usuyukiの力上昇
    全体を通して

    View Slide

  59. 59
    ピクシブ百科事典の
    改善
    メンターの
    tadsan
    インターンの
    usuyuki
    usuyukiの力上昇
    全体を通して

    View Slide

  60. 60
    おわり
    ご清聴ありがとうございました!

    View Slide