Silex」タグアーカイブ

「選定した技術が1年で死んだ話」の1年後

1年半前に以下のような記事を書きました。その記事が前提となり、その続きになります。

対応の方向性

2018年末ごろから Symfony 4.2 への移行を始めました。Symfony 4 についての日本語情報はかなり少なかったので、数少ない日本語情報に感謝をささげながら、基本は Symfony 公式のドキュメントを Google翻訳にかけて読みながら対応しました。

※ 得られた知見を本ブログ内別記事にまとめています。

Silex 1.x → Symfony 4.2 への移植の実際

現状の2代目システムの実装としては Silex 1.x での実装となり、依存性注入の仕組みは Pimple というアレイアクセスベースのシンプルなサービスロケータ、クラス構成の主要な部分としては、コントローラ、サービス、リポジトリのインタフェース、リポジトリ、DoctrineDBALで読みだしたレコードを保持したりバリデーションが実装されていたりするレコードオブジェクトという構成になっていました。

コントローラとビュー

まずはコントローラの移植になりますが、基本的にはこの部分が実コードでは一番重いものになりました。Silexのルーティングから Symfony のアノテーションを使った物に置き換えていく作業は主に以下の内容になりました。

  • SilexのクロージャベースのルーティングをSymfonyのアノテーションベースのルーティングに書き換える作業を進めます。どちらか一方でしか出来ない機能という物がなく、淡々と対応すれば完了しました。ただし物量が多い。
  • Requestオブジェクトなど要素要素の機能はもともとSymfonyコンポーネントを使っていたSilexなのもあり、そのまま使い続ける事が出来る。
  • リダイレクトなどは関数名が変わってくるので調整する。
  • ビューはTwigがそのまま使えるのでセッションに関する扱いだけ調整すればそのまま動作する。
  • Pimpleを使っていたDIは全てコンストラクタインジェクションに変更。あとはSymfonyが勝手にサービスを注入してくれる。

ルーティングは書き換えになりますがコントローラ内のアクション処理としては9割そのままコピペで動いた印象です。残りの1割を書き換えていきました。

サービス

ここでいうサービスは所謂ビジネスロジックです。サービスクラスは一部Pimpleのサービスロケータを引き回している部分が有ったので全てコンストラクタインジェクションか、コントローラから呼ばれる際のメソッドインジェクションに変更しました。ログ出力に関しても同様に Psr\Log\LoggerInterface を注入するように変更。それ以外はSilex独自の機能への依存がほとんどなく、ほぼそのまま動作しました。

リポジトリとレコードオブジェクト

リポジトリの実装はDoctrineDBALの存在にがっつり依存していましたが、これはSymfonyでも同様に利用できるので問題になりませんでした。ほとんどの処理がそのまま動作しました。

テスト

PHPUnitで実装されたテストは全体の2~3割程度に調整が必要になりました。この背景として元々使っていた phpunit/dbunit の開発終了が有ります。
https://github.com/sebastianbergmann/dbunit

データベース周りのテストを行う際にレコードの取得結果を比較したり、YAMLファイルで定義したデータをDBに流し込んだりリセットしたりなどの機能を提供しているライブラリなのですが、これが開発終了し新しいPHPUnitに対応していかなくなる事態になりました。

色々方法は考えたのですが、phpunit/dbunit の提供する機能のうち、実際に利用してたものだけについて互換実装を作り込んで使う事にしました。YAMLファイルを読み込んでDBに書き込む機能、欠けた部分のある連想配列を欠けていない部分のみで比較する機能などを関数のインタフェースに互換性を持たせながら実装して、これは1ファイル1クラスのシンプルな互換機能になりました。もちろん PHPUnit 8系で問題なく動作する物になっています。

結果

期間としては大体3か月程度でSymfony 4.2 への移植が完了しました。これは想定よりもだいぶ短い期間でした。併せて PHP 7.1 から 7.2 への移行を行ったり様々な追加機能を載せたりしていますが、現状問題なく運用し続けています。上手く行った要因としては以下が有ったのかと思います。

  • Silex 自体の開発が終了したと言っても Symfony は健在なので、置き換えが必要なクラス、機能が最小限で済んだ。結果論ではあるがSilex採用時の「Symfonyコンポーネントを使ったフレームワークにしよう」という選択に助けられた。
  • もともとサービスクラス、リポジトリ、レコードオブジェクトがほぼSilexに依存しておらず、依存関係も依存性注入を使った作りになっていたので移植コストが低かった。再設計時の設計が(ベストかどうかはともかく)悪くなかった。
  • 初代システム(1ページ1PHPファイル)とは違って2代目システムはテストコードが有ったので、動作確認のコストが抑えられた。テストが書かれていなかった部分やテストは書かれているもののカバーしきれていなかった動作、その他もろもろでバグは出たものの、それでもテストが無い状態よりははるかに楽になった実感が有る。

結論

  • フレームワークの機能を便利に使いつつも 、それに依存しすぎない疎結合な設計になっているとフレームワークが開発終了に見舞われてもコードの大部分をそのまま動かす事が出来る。それまで「疎結合大事って言ってもライブラリ、ユーティリティ的なクラス以外はあんまり再利用したりしなくない? テストが書きやすくなるのは間違いないけど」と思う部分も有ったが、テストが書きやすいだけではないフレームワークに対する疎結合の重要性を体感した。
  • さすがにフレームワークが変わるレベルだと動かなくなるテストコードも少なくないので「元と同じテストが全部通ればOK」とはならないものの、とはいえ有るのと無いのでは全く違う。テストコード大事。

選定した技術が1年で死んだ話

今年の夏頃から、特にサービスとして出すわけではなく、社内で使っているシステムのリプレースを行う事になりました。主な目的はレガシーすぎる設計をある低度モダンにする事、そして他のシステムと連携出来るようにする事、です。

対象のシステム

見積書や請求書などを管理・発行している。機能はそれなりに多いがUI操作はFormベース、テーブルタグで諸情報を表示するシンプルな物。ノンフレームワークで1画面1PHPファイルな古き良き時代のコード。おそらく10年ぐらい?稼働している。当初はPHP 5.1、PostgreSQL 8.x系だったが、現在はPHP 5.6とPostgreSQL 9.6で稼働しています。

その他の社内システム

かつてはノンフレームワークだったり、太古のバージョンのCakePHPだったり、PHPが4系だったりしたが、概ねCodeIgniter 3系最新版 + PHP 5.6~7.1 + PostgreSQL 9.6 / MySQL 5.6 に統一されている(統一する改修を進めてきた)。

対象のシステムをどうするか

これまで通りCodeIgniterでもよかったのですが、そろそろ新しい物を触ってみようかという事で以下のようなことを検討しました。

  • バージョン4以降の Laravel など、Symfonyコンポーネントを活用するフレームワークに関する話題が流行っているように見える。PHPカンファレンスなどでも耳にする事が多い。
  • 軽量、軽快なCodeIgniterですら現状フルに機能を活用している訳ではない。URLのルーティング、エラーハンドリング、ログ出力、設定ファイル読み込み、Twigテンプレートエンジンの呼び出し、ORマッパーまでは使わない程度のDB機能、セッション制御、Composer対応が有れば十分に思えた。
  • Laravelは現行LTS期間が残り1年ほどと、すぐに次の更新を考えないといけないタイミングだった。
  • 都合上データベースの構造には大きく手を入れない方向。DBのカラム名が実装にかかわってくるタイプのフレームワークは使用しない。
  • Silexが使うDIコンテナ(もしくはサービスロケータ)の pimple はシンプルで分かりやすかった。
  • Symfonyコンポーネント製、必須機能だけを備え必要な物はSymfonyコンポーネントで追加するスタイル、数年間開発が続いている事、DIの仕組みが分かりやすい、DoctrineDBALでシンプルにDBアクセスが出来る Silex はそこそこ良いのでは。
  • Symfony自身が関わっているのでそれなりにしっかり開発されているように思えた。
  • あの EC-Cubeでもバージョン3で採用している。Web上で見かけた採用理由も近いものが有った。

実際の開発と設計

これまではほぼほぼ自分自身で開発してきましたが、このシステムに関しては社外の協力要員の方に実装して頂きました。私より実力が全然有る方なのでコード品質への懸念はあまりありませんでした。

設計はシステムを Controller, Service, Repository, Entity といったオブジェクトに分割した、ドメイン志向設計風の物になりました。Silexはマイクロフレームワークなので、それこそ1ページ1PHPファイルにしてしまう元とあまり変わらないような実装も出来てしまうのですが、だいぶモダンな設計になりました。これまでの1ページ1PHPなシステムではほぼ不可能だったテストコードも書けるようになりました。

所感

先に述べたように、マイクロフレームワークである分、どうやって設計するかがかなり重要になってきます。設計力の有る方でしたので比較的綺麗な設計になりました。再利用性もそれなりに有るように思います。ただしもし設計がまずい場合、マイクロフレームワークである事がマイナスにしかなっていなかったかもしれません。

Silexがマイクロかどうかについては、普通に使う分には確かにマイクロなのですが、もともと高機能・フルスタックなSymfonyですので、機能を追加するたびにあれよあれよとComposer上の依存パッケージが増えていきます。困ったらコードを読める、かつ読みやすいのはやっぱりCodeIgniterの良い所でした。

突然の死

突然ではなかったのかもしれませんが、11月に Silex のサポート終了(EOL)が発表されました。2018年の6月にサポートが終了との事で、開発開始から1年で移行先の無いフレームワーク終了のお知らせとなってしまいました。

この技術選定はダメだったのか

Q. 素直にLaravelにするべきだったのか?
A. 今回はSymfony 4で互換性を切るという趣旨でのサポート終了であるらしく、Symfonyコンポーネントと関わりのあるLaravelについても、今までの事を考えても今後もそれなりに大きなアップデートを適用していく必要が有ります。もっとも、全く別のフレームワークを使うか、そのままでメンテをし続けていく必要が有る Silex より Laravel の方が未来が明るいです。

Q. マイクロフレームワークがいいならLaravelベースのLumenにすればよかったのでは
A. これは有るかもしれません。

Q. 同じマイクロフレームワークで middleware の仕組みが使える Slim は?
A. Slim は機能面で Silex 以上に薄い、との意見を見かけた事により実装しやすそうな Silex に流れましたが、Slim の方が未来が有った可能性は有ります。ただ、本当にこっちの方が未来が有ったかは今でも分かりません。

Q. 開発効率は良かったのか
A. これも何とも言えません。実装担当の方はLaravelもFuelPHPも経験者であったので、他のフレームワークでもそこまで大きな違いはなかったのではないかと思います。ただ、これは実装者の技術によるところでも有りますが、フレームワーク独自のしきたりをそこまで覚えなくていい、見通しのいい実装する事はできました。

Q. サポート期間が短い?
A. これまで数年に渡って開発されてきたので十分長くサポートされてきたと言えます。ただ、今このタイミングで採用したのは私の見る目が悪かった。もっとも、サービスとして外部に提供するわけでもないシステムなので、これまでがそうであったように使おうと思えば3年でも5年でも使えるでしょう(良くは無いです)。

Q. 目的は達成できたか?
A. クラスの無かったコードは比較的モダンな設計になり、テストコードが書けるようになり、今後ほかのシステムと連携させていく上でも拡張可能な物になりました。

結論

あまりこうだ、と言えることは無いです。長々と読んでもらって申し訳ないですが、どこがどう教訓とも何とも言えないです。Lumenの方が良かったかもしれませんが、結果論です。ひとまず、使える物は作れたので使っていく方向です。CodeIgniter安定!でもよかったのですが、CodeIgniterはドメイン志向設計な書き方にはあまり向いていないように思うので、その辺りは新システムのコードレビューなどでドメイン志向のような物の勉強になりました。

そうですね、「フレームワークの寿命は選ぶ時点ではなかなか分からない」「寿命を気にするならベストだと思う物より、皆が使っているベターを選ぶべき」辺りでしょうか。以上です。

2018年6月1日 – 続きを書きました。
https://www.sodo-shed.com/archives/13231