「良いコード/悪いコードで学ぶ設計入門」を買いました
皆様GWはいかがお過ごしでしょうか。
僕は地元のスーパー銭湯に約20年ぶりぐらいに行ってノスタルジーと風呂に浸ってました。サウナは良いです。
本を買いました
さて、GW2日目にこんな本を買いました。
良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方 | 仙塲 大也 |本 | 通販 | Amazon
クソコード動画等で知られるミノ駆動さんがこの度出された書籍です。
同じチームの方に発売直前トークイベントがあることを教えていただいて、イベントでも面白い話がたくさん聴けたので発売日に買いに行きました。
予約段階で1万部、発売から1週間で4刷とのことで異例尽くしの1冊です。
ざっくりした感想
まだ読んでいる途中ですが、悪いコードの例を元にどういう観点で修正していくのかがわかりやすくまとめられていてとても読みやすいです。
個人的には第6章の「条件分岐」や第8章の「密結合」辺りが特に刺さりました。
前職で担当していた部門のコードがまさに悪いコードの例として載っているものにとても似ていたからです。
ネストしまくって可読性の悪いif文や単一責任どころかいくつ責任を抱えているのかわからないクラスなどなど。
当時この本に出会っていればもっとバグを減らすことができたのかも、と思いました。
今担当しているプロダクトではこの本の内容を参考にしながら実践していきたいと思います。
チームで共有したい
加えて感じたことは個人で意識するだけでなく、チーム全体で「良い設計」についての認識を共有することが大事、ということです。
全員が同じ視点を持っていればお互いに指摘し合うことができますし、チームとしてブレずに改善することに集中できるのではないかと思いました。
先述のトークイベントの中でも「逆リファクタリング」という話もあり、気がついたら悪いコードになっていた、なんて現実的に起こりうる話です。
そうならない為にも、チームとして設計方針の共有を図ることは大事だと思います。
まだまだこの本に書かれている内容を実践できているとは言えないレベルですが、自分とチーム両方とも生産性を上げる為にも頑張りたいなと思います。
社内LT会で発表しました
昨日、社内でLT会があり発表させてもらいました。
毎月開催されており今までは聴く側だったのですが、今回始めて登壇してみました。
自分自身、発信力だったり考えを整理して伝えるという点が苦手ということもあり、少しでも改善できれば...という思いです。
登壇資料
発表に使った資料がこちらです。
内容について
今回は Firebase Authentication について発表しました。
要点は以下のとおりです。
* Firebase Authenticationでソーシャルログイン実装してみた * めっちゃ簡単に実装できる * けど実務で使えるかというと微妙
業務時間外に個人で色々いじっていたのをまとめてみたって感じです。
↑のスライドの内容をこのブログでも改めて整理してみようと思います。
NuxtでFirebase Authentication使ってTwitterログインを実装してみた
今回はNuxt上で試してみましたので こちらのパッケージを使いました。
yarn add @nuxtjs/firebase
Twitter Developersでのアプリの作成やFirebaseのアカウント作成・Twitterログインの有効化等については割愛します。
nuxt.config.jsにFirebaseの設定を追加します。予め環境変数にFirebaseの設定を追加しておきます。
modules: [ '@nuxtjs/firebase' ], firebase: { config: { apiKey: process.env.FIREBASE_API_KEY, authDomain: process.env.FIREBASE_AUTH_DOMAIN, projectId: process.env.FIREBASE_PROJECT_ID, storageBucket: process.env.FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID, appId: process.env.FIREBASE_APP_ID, measurementId: process.env.FIREBASE_MEASUREMENT_ID }, services: { auth: true } }
準備ができたらログインボタンを用意して、ボタンのclickイベントを設定します。
async signinWithTwitter() { const provider = new this.$fireModule.default.auth.TwitterAuthProvider(); await this.$fire.auth.signinWithRedirect(provider); }
@nuxtjs/firebase
ではthis.$fire
でサービスのインスタンスに、this.$fireModule
でFirebaseのモジュールにアクセスできます。
TwitterAuthProviderをsigninWithRedirect()
の引数に渡してあげるだけでボタンをクリックした時にTwitterのアプリ認証画面に飛びます。
ちなみにsigninWithRedirect()
以外にもsigninWithPopup()
という関数もありますが、モバイルではポップアップがブロックされることがある為、Redirectの方が推奨のようです。
Twitterのアプリ認証画面で認証が終わると元いたURLにリダイレクトされます。
この時にユーザー情報を取得する為にgetRedirectResult()
を呼び出します。
async mounted() { const user = await this.$fire.auth.getRedirectResult(); console.log(user); }
そうするとこんな感じでユーザー情報が取れます。
という訳で実装は以上で完了です。めっちゃ簡単です。
実際のところどうなのか?
ですが、正直実務ではあまり使いたくないというのが本音です。
なぜならCredentialsをフロントエンドで扱っているからです。
想定されるユースケースとしてログインしたユーザーのAccessTokenでAPIを叩く、というものが考えられると思います。
実際 こちら のTwitter APIのケースを見るとアプリのトークンだと15分間で1500回までの制限があるのに対し、ユーザーのトークンだとユーザーごとに15分間で900回です。
手元のローカル環境で試す分にはどちらでも大差ないですが、多数のユーザーがアクセスすることが想定されるサービスの場合は制限に引っかかってしまう可能性があります。
なのでユーザーのトークンでAPIを叩きたいところですが、一度DB等に保存して使うことになると思います。その時にFirebase Authenticationを使うとフロントエンドからバックエンドにCredentialsを渡すことになり、これがどうにも違和感があります。
そもそもCredentialsをフロントエンドで扱いたくないのでバックエンドで処理したい。
ですが色々調べてみた結果現状Firebase Authenticationを使う以上、Credentialsをフロントエンドに渡さずに処理する方法は無いようです。
なので大人しくバックエンドを実装する方がベターかと思います。例えばLaravelなら Socialite を使えば同様のソーシャルログインは実装できます。
まとめ
以上、LT会で発表した内容について書いてみました。
いろんな記事を見ても「簡単に実装できます!」という点ばかりで、そこから踏み込んだ内容に触れているものが無かったのでまとめてみました。
また次回以降もLT会には積極的に発表していこうと思います。
Laravelの長所について
前回Laravelの最低限の環境構築をしたので今回もLaravelについて書こうと思います。
前の会社でもLaravelはほんの少しだけ使ったのですが、メインで使っていたフレームワークはCakePHPでした。
Laravelについて社内に詳しい人がいなかったこともあり、イマイチその良さを感じられませんでした。
が、最近ようやく少しずつ実感できてきたのでその辺りをまとめてみたいと思います。
依存性の注入が簡単にできる
結論から言うとこれがLaravelの一番の長所のように思います。
クラスAがクラスBを読み込む時、クラスAはクラスBに依存していると表現します。これはBに変更があればAの処理内容にも影響が及ぶことを意味します。
その為、できる限り特定のクラスやモジュールに依存しないように設計することが望ましいと考えられます。上記の例でいうと、クラスBを変更してもクラスAへの影響が無い、またはできる限り少なくすることが理想です。
それを実現する為にはどうすれば良いかと言うと、必要以上の知識を持たせないようにすることが重要になります。つまりクラス同士の関係はできるだけ抽象的にし、実際の処理や必要な知識については外部から注入してあげることで依存度を下げることができます。これが 依存性の注入 (Dependency Injection / DI)
と呼ばれるものです。
Laravelはサービスコンテナという仕組みを使ってこの依存性の注入が簡単に実現できます。例えば、
class Hoge { /** * @var Fuga */ private $fuga; /** * @param Fuga $fuga */ public function __construct(Fuga $fuga) { $this->fuga = $fuga; } }
とすることでHoge
クラスにFuga
クラスを注入することができます。タイプヒントのみで自動的に依存解決してくれます。
こうすると仮に違うクラスやモジュールを使うことになっても簡単に交換可能になります。Hoge
クラスでFuga
クラスの知識を持っている必要はありません。
更に抽象に依存させることでLaravelの恩恵をより受けることができます。
interface HogeInterface { /** * @param FugaInterface $fuga * @return Piyo */ public function hogehoge(FugaInterface $fuga): Piyo; }
interfaceを注入することで依存度はグッと下がります。実際の処理内容は↓のように実装クラスに書くことで注入される他のclassやinterfaceについての知識を持つ必要が無くなります。
class Hoge implements HogeInterface { /** * @var PiyoRepositoryInterface */ private PiyoRepositoryInterface $piyo; /** * @param PiyoRepositoryInterface $piyo */ public function __construct(PiyoRepositoryInterface $piyo) { $this->piyo = $piyo; } /** * @param FugaInterface $fuga * @return Piyo */ public function hogehoge(FugaInterface $fuga): Piyo { return $this->piyo->piyopiyo($fuga); } }
ここではPiyoRepositoryInterface
というリポジトリを使ってデータを取得するんだなということはわかりますが、どんな方法でデータを取ってくるのかについての知識はありません。DBから取ってくるのか、外部APIから取ってくるのか等はPiyoRepositoryInterface
に結合される実装クラスによって変わります。が、それをHoge
及びHogeInterface
が知っている必要はありません。
同様に引数のFugaInterface
もどのような型の値が渡ってくるかはFugaInterface
に結合されるクラスに依ります。引数を修正する必要が出た場合でもFugaInterface
に結合するクラスを変えるだけでOKです。
また、interfaceに実装クラスを結合するにはServiceProvider
を使います。
class AppServiceProvider extends ServiceProvider { public function register() { $this->app->singleton(HogeInterface::class, Hoge::class); } }
こうすることでHogeInterface
を呼び出した時に自動的にHoge
を結合してくれます。
このように、依存性の注入が簡単に実現できるところがLaravelの最大の長所だと思います。
単体テストが書きやすい
上記の依存性の注入に伴うメリットの1つとして単体テストが書きやすいということが挙げられます。
抽象に依存させ、しっかりと責務を分離した状態を維持できればそれぞれのクラスが持つ責務についてのみテストを行うことができるからです。
そうすることで各クラスの質を担保することができ、バグを減らすことにも繋がります。
class HogeTest extends \Tests\TestCase { /** * @return HogeInterface */ public function test__construct(): HogeInterface { $hoge = $this->app->make(HogeInterface::class); $this->assertInstanceOf(Hoge::class, $hoge); return $hoge; } /** * @depends test__construct * @param HogeInterface $hoge * @return void */ public function testHogehoge(HogeInterface $hoge): void { $fuga = new FugaInterface('fugafuga'); $result = $hoge->hogehoge($fuga); $this->assertInstanceOf(Piyo::class, $result); } }
こんな感じでテストが書けます。僕は実務で何かクラスを実装するときはまずinterfaceを作り、対応するclassのガワを作った時点で単体テストを作成することを意識しています。ガワさえ作れば上記でいうtest__construct
の部分は書けるからです。この辺はTDD(テスト駆動開発)に関わるところなので改めて記事にできればと思います。
まとめ
以上、Laravelの最大の長所とも言える依存性の注入について書いてみました。
まだ知識が浅いので間違っている箇所があればご指摘いただけると幸いです。
冒頭で書いた通り前の会社ではCakePHPを使っており、その時はこうした概念すら知らなかったのですが、設計を考える際に非常に強力な武器になるなと実感しています。
引き続き実践を重ねながら少しでも良い設計・実装ができるように努めていきたいと思います。
Laravel環境構築(最低限のみ)
今日はタイトルにある通り、Laravelの環境構築について書いてみたいと思います。
普段の業務でもLaravelを使用していますが、新しくプロダクトを作るとかでもない限り開発環境を一から作ることってなかなか無いと思います。
実際入社した際にもDockerによってそこまで詰まることなく環境構築できました。
なので、改めて自分でやってみようということで書いていきたいと思います。
今回のゴール
- LaravelとMySQLによるローカル環境を構築する
Docker
今回はDockerを使って構築していきます。
Dockerはコンテナ型の仮想化技術であり、ホストOS上で動作しているDocker Engineの上にコンテナと呼ばれるアプリケーションの実行環境を構築するものです。
同じ環境を速やかに構築・共有できるという点でチームでの開発においては今や不可欠な技術の一つではないかと思います。
Docker とは開発者やシステム管理者が、コンテナでアプリケーションを 構築(build)、実行(run)、共有(share)するためのプラットフォームです。アプリケーションをデプロイするために、コンテナを利用する事をコンテナ化( containerization )と呼びます。コンテナは新しくありませんが、コンテナを使えばアプリケーションのデプロイがより簡単になります。
引用元:Docker ドキュメント日本語化プロジェクト | Dockerの概念
今回はDocker HubにあるPHP公式のDockerイメージを使用します。
PHP公式だけでもいくつか種類がありますが、その中でもPHPとApacheが一緒になったイメージを使ってDockerfileを書いていきます。
FROM php:8.0-apache COPY --from=composer:latest /usr/bin/composer /usr/bin/composer RUN apt-get update && apt-get install -y \ git zip unzip \ && docker-php-ext-install pdo_mysql ENV APACHE_DOCUMENT_ROOT /var/www/html/public RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
1行ずつ解説していきます。
FROM php:8.0-apache
PHP8.0とApacheが一緒になったイメージを指定しています。
バージョンは各自お好みで指定してください。
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
マルチステージビルドを使ってcomposerをインストールしています。
マルチステージビルドとは1つのDockerfileの中で複数のビルドを行うことができるというものです。ビルドされたステージから他のステージにコピーすることができ、不要な部分はそのまま破棄されます。 そうすることで最終的なファイルサイズを小さくすることができます。
ここでは--from=composer:latest
でイメージを指定しています。そしてcomposerイメージの/usr/bin/composer
の中身をビルドされたPHP-Apacheの/usr/bin/composer
にコピーしている、ということです。
RUN apt-get update && apt-get install -y \ git zip unzip \ && docker-php-ext-install pdo_mysql
必要なパッケージをインストールしています。
apt-get update
でパッケージリストの更新を行い、apt-get install
でインストールを実行しています。-y
はすべてYes
と回答するオプションです。
docker-php-ext-install
はPHP拡張機能を入れるためにPHP公式が提供しているコマンドです。
ENV APACHE_DOCUMENT_ROOT /var/www/html/public RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
デフォルトではApacheのDocumentRootは/var/www/html
になっています。それをvar/www/html/public
に変更する為の処理になります。
こちらもPHP公式がDocker Hubに書いています。
docker-compose.yml
続いてdocker-compose.ymlを書いていきます。
version: "3.9" services: app: build: context: . dockerfile: ./docker/php/Dockerfile volumes: - ./src/:/var/www/html/ ports: - 8080:80 db: image: mysql:5.7 ports: - 3306:3306 volumes: - db_data:/var/lib/mysql environment: - MYSQL_DATABASE=mysql - MYSQL_USER=user - MYSQL_PASSWORD=password - MYSQL_ROOT_PASSWORD=root volumes: db_data:
先程作成したDockerfileをapp
のbuild.dockerfile
に指定しています。
また、ホストの8080番ポートとコンテナの80番ポートを紐付けています。こうすることでブラウザでlocalhost:8080
にアクセスしたときにコンテナの80番ポートに繋がるように設定できます。
volumes
でホストの/src
ディレクトリとコンテナの/var/www/html
ディレクトリをリンクさせています。
また、db
についてはDockerfileではなくMySQL5.7の公式イメージを使用しています。db.environment
で初期ユーザーの設定をしている他、volumes
で指定したボリュームをdb.volumes
でコンテナと紐付けています。こうすることでdocker compose down
でコンテナを破棄しても再度立ち上げた時にデータを永続化することができます。
ここまでできたら、
docker-compose build --no-cache docker compose up -d
でコンテナを起動します。
Laravelのインストール
あとはLaravelをインストールするだけです。
docker compose exec app composer create-project --prefer-dist laravel/laravel .
app
コンテナの中でLaravelをインストールします。
しばらく待つとインストールが完了するのでlocalhost:8080
にアクセスしてみると、
無事にLaravelの初期画面が表示されました。
まとめ
以上、Laravelの環境構築についてまとめてみました。
ただ、タイトルにあるように本当に必要最低限なので実際にはもっと設定が必要になると思います。
パッケージや拡張機能も入れるでしょうし、NginxをWebサーバーとして立てる構成もあります。
この記事ではとりあえず最低限の環境構築というところを目指したので、上記のような追加の構成については追々記事にできればと思います。
ブログ始めました
はじめまして。191cmと申します。
タイトルにあるようにブログを始めてみました。プログラミングの勉強の備忘録として使っていきたいと思います。
自己紹介
株式会社スマレジでwebエンジニアとして働いています。エンジニア歴は2年半程。
使用している言語は PHP (Laravel) / JavaScript (Vue.js / Nuxt)です。
まだまだ日々学ぶことだらけですが、新しい知識を増やしてできることが一つずつ増えていくのが楽しいです。
最近気になっているのはNode.jsで、個人的に色々やってみようかなと思っています。フロントもバックもJSで書いたプロダクトとか作ってみたい。
プログラミング以外で好きなものは音楽です。20歳の頃から10年以上に渡ってレコード収集を続けています。
あと最近は漫画も読みます。「左ききのエレン」「トリリオンゲーム」辺りが好きです。
散歩も好きでどこへともなくブラブラすることが多いです。リモートワークになってから運動不足なので更によく歩くようになりました。
ようやく暖かくなってきて散歩がより楽しい季節になりました。
スマレジについて
僕が勤めている株式会社スマレジは名前の通りタブレットによるPOSレジサービスを提供している会社です。
POSレジ以外にも勤怠管理サービスのスマレジ・タイムカード、飲食店向けのスマレジ・ウェイター等のサービスを展開しています。
スマレジに入って良かった点
さて、ここからはスマレジに入ってここが良いなと感じている点について。
僕は2022年1月に入社したので3ヶ月が経過したところですが、振り返ってみたいと思います。
尚、チームによって異なる部分もあるかと思います。あくまでも1つの意見として読んでいただけると幸いです。
1. エンジニアドリブン 2. 柔軟な働き方 3. 様々な技術に触れられる
エンジニアドリブン
これは前の会社と一番違うと感じているところです。
前の会社では営業や企画部門から仕様が降りてきて、基本的にはそれに沿って開発を進めていくスタイルでした。
スマレジではエンジニアが仕様を決めて開発も行います。裁量は大きいと思います。
勿論、「なぜそうするのか」という説明責任は生じます。何でも自由にできるわけではありません。
しかし開発部門が主体となってプロダクトを作っていくことができるのは何より魅力的ですし、楽しいです。
「なぜ」がしっかり説明できればより前向きな議論がしやすいという点でも良い環境だと思います。
僕自身はまだまだ言語化力が弱い点が課題なので鍛えていきたい所存です。
柔軟な働き方
基本的にリモートワークです(必要な場合は出社することもあります)。
また、出社時間は午前8時〜10時の間であれば自由なので日によって変えることもできます。
また、まだ取ったことはありませんが祝日を好きな日に振り替えることができるマイホリデー制度というものもあります。
働き方についてはかなり柔軟だなと感じています。
様々な技術に触れられる
僕の所属するチームはLaravel × Nuxtというモダンな環境で開発しています。
勿論開発環境はチームやプロダクトによるところが大きいですが、新しい技術に触れられる機会は多いんじゃないかなと思います。
また月一でLT会も開かれており、他のチームがやっていることや個人開発等、普段触れる機会が少ないことも含めて様々なテーマの発表が聞けるので視野も広がったように思います。
言語面だけでなく、クリーンアーキテクチャやドメイン駆動設計等の設計面でも日々学ぶことは多いです。
こちらもまだまだ完全に理解したとは言えないレベルですが、少なくともスマレジに入社してから改めてエンジニアって楽しいなと感じました。
まとめ
以上、初エントリはスマレジに入って良かったと思う点3選でした。
次回から業務や独学で学んだことなどを中心に投稿していくので引き続きよろしくお願いします。