---
# System prepended metadata

title: 考えたこと

---

# 考えたこと
## 前提
### 銀の弾丸は存在しない
- https://ja.wikipedia.org/wiki/%E9%8A%80%E3%81%AE%E5%BC%BE%E3%81%AA%E3%81%A9%E3%81%AA%E3%81%84
- 全てのチームで上手く行くプラクティスは存在しない
- そのタイミングのそのチーム、プロダクトに合った解決策を模索する必要がある
### コードの品質は何もしないと下がり続ける
- ≒技術的負債
- 基本的に品質の向上は機能追加の要望に劣後する
- 意図して負債を返済する意識、プラクティスを実践する必要がある

## テストについて
### 自動テストが書きやすい設計≒良い設計
- テストがしやすい設計、実装は、良い設計であることが多い
    - それだけですぐに良い設計にはならない事に注意
- 逆にテストがしづらい設計を考えてみる
    - ランダム、動的な値への依存
        - 例：Date.now(), UUID.randomUUID()
    - 多くの依存を持つクラス
        - 依存対象が多いとそれだけでテストを書くのが面倒になる
        - Service層のテストが書きづらい原因はこれ
    - システムの外部に依存するクラス
        - データベース、ファイルシステム、クラウドストレージ等
### 自動テストを書くのも技術の1つ
- すぐにはできないかもしれない
- 覚えなきゃいけないテクニックがそこそこある
    - モック、スパイ、パラメタライズドテスト等
- 理想はテスト駆動開発（＝テストを先に書いて実装を後から書く、テストと実装のループで設計を改善する）
    - 参考：https://www.amazon.co.jp/dp/B077D2L69C/

## 実装について
### 重複するコードが生まれたら、共通化できないか考える
- 既存の処理で代用できないか考える
- 重複する部分に共通する意図は何か考える
    - 無理やり共通化するとかえって混乱する場合もある
    - コードよりも、意図に注目する
- 共通化の方法はいくつかある
    - クラス内のprivate関数、新しいクラス、パッケージ等
### 名前で意図を表す
- コードをじっくり読まないと意図が分からないのは危険
    - 読み手によって意図が誤解される可能性が高まる
- インターフェース、抽象で意図を表現する
    - 「〇〇な条件を満たすkeyがuserでvalueがユーザー名のマップを配列に詰めて返す」のは実装
    - 「〇〇なユーザーを取得する(=getHogehogeUsers)」のが意図

## ServiceとRepositoryについて
### Serviceでは業務の流れ、仕様を表現する
- 細かい実装をさらけ出しすぎない
- 「どのように値を取得するか」の詳細はRepositoryの実装に任せる
    - 例：TypeORMのQueryBuilder等
- 例外をどのように投げるかも仕様の一部
    - 例：例外が返ってきうる箇所では必ずtry-catchする等
    - 単なるErrorよりもClientStatusCode等で具体的に例外を表現する
- エラーメッセージでトレーサビリティを意識する
    - リクエスト時のIDを吐き出す
    - どのような処理が失敗したか、分かるようにする（単なるfailより、fail to get hogehogeとか）

### Repositoryのインターフェースで、どのようなデータの処理ができるのかを表現する
- インターフェース上で実装に寄り過ぎない
    - 細かい実装が変わってもインターフェースが変わらないのが理想（必ずそうなるわけではない）
    - 例：DBの設計が変わった、配列の処理をfor文からmapに変えた等
### Repositoryの実装で具体的にデータを取得する処理を書く
- typeORMのQueryBuilderとかはここに書く

## その他
- PR上で「修正内容の概要」「修正した背景」「修正の意図（なぜそうしたか・しなかったか）」をレビュワーが伝わるように書く
    - 今回自分はチケットとかイシューがあったわけではないので、ここで情報が完結するようにしてた
    - レビュー時間短縮＝デリバリまでのリードタイム短縮＝競争力に繋がる

## 今回考えなかったけど考えた方が良さそうなこと(考えてたらごめんなさい)
- Params, Entity, VOのそれぞれの役割
    - パッと見てる感じ、以下のような理解だけどもう少し明確にコードで表せると更に良さそう
        - Params ... 外部（HTTP通信）から受け取ったパラメータ
        - Entity ... Databaseに格納されるオブジェクトの形
        - VO ... クライアントに返却されるオブジェクトの形
    - もう少しシステムが複雑化してきたらこれらとは別のDomain Objectを作成して業務ロジックを集約していきたい
- 1Repository内で複数DBを扱うことについてのルール
    - サブクエリ、JOIN等やっちゃダメなわけじゃないけど、ルールがないと無秩序になりやすい
- 取ってきたクエリの結果をどこでVOに変換するか
- async/await, どこでawaitするか
    - 現状Repositoryでawaitしてたりしてなかったりする
    - 本来awaitが必要ないところでもawaitしてないか？
- Factoryを作るか否か、Factoryを作るとしたら作るのはEntityかVOか