Golang APIサービスの構築と管理


スケーラブルで、安全で、高い利用可能なAPIサービスを構築することは簡単な仕事ではありません.それはきれいでテストされたcodebase、堅牢な基盤、リアルタイムの可視性とシステムのふるまいとパフォーマンス、エラー報告とインシデントトリガシステムへのモニタリングをカバーしなければなりません、そして、より重要なことに、人々は即座の問題を解決するためにパッチを出す代わりに根本原因を掘り起こすことを目的とするエンジニアリング文化を育成する必要があります.技術の負債に注意し、短期保守性と長期保守性のトレードオフを理解する.
ヒアアットchowbus , 私たちは数年の間、ゴラン/ルビーでマイクロサービスを構築してきました.一方、私はコミュニティと議論したいいくつかの作業慣行/パラダイムもあります.この記事では、我々は生産コードベースから抽出された簡略化されたバージョンであるBoilerPlateを共有し、プロジェクトの品質を維持し、その寿命を延長するのに役立ついくつかの基本的なアイデアについて話します.
どうぞhttps://github.com/JingIsCoding/api-server-boilerplate 簡単なセットアップ.ReadMeファイルがCodeBaseの異なる側面についてのより深い紹介を提供するので、我々は記事のBoilerplateの詳細に飛び込むつもりはありません.

階層とデータフロー

アプリケーションをビルドするときの1つの一般的なルールは、モジュール/レイヤを疎に結合し続けることです.Golangのコンテキストでは、モジュール/レイヤー間のインターフェイスを常に宣言したいと思うかもしれません.
別の側面は、コールスタックをパスするオブジェクトについては注意してください.これは、すべてのコンポーネントを暗黙のうちにバインドし、別のエンドポイントを追加する必要がある場合、基礎となるロジックを再利用するのを難しくします.
アプリケーションが稼働していると、クライアントや他のサービスを実行すると、APIを消費するために起動すると、それから我々は、既存の行動を変更し、新しいビジネスニーズを満たすためにより多くの機能を提供する必要がありますが、異なるバージョンで実行される可能性があります既存のクライアントを壊す必要はない本当の挑戦をしています.以下のセクションでは、あなたのアプリケーションを健康に保ついくつかの一般的な考えについて話します.

クリーンコード
あなたがそうしないならば、私は強くあなたに時間をとって、この本を読むことを勧めますhttps://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882 .
この本には多くの良い点があります、1つの特定のものは十分に強調されることができません、変数、関数とモジュールの命名に注意を払っていて、あなたが何かよりよく考えることができるならば、再訪し続けてください.ちょっとここでいくつかのケースに言及してください.

できるだけ特定しなさい.
項目やデータのような変数名を使用する代わりに
for _, item := range productService.GetProducts() {
   ...
}
ちょうどそれがそうであるように、名前をつけるほうがよい
for _, product := range productService.GetProducts() {
   ...
}
そのように、あなたはすぐにあなたが何を扱っているかを知って、それを参照しますproduct ブロック内.

単一責任
ビジネスロジックと関数の拡張の複雑さのために、避けられないことに、ソースファイルの長さと特定の長さの長さは、1つのファイルが長さが1000行で、1つの関数がネストされたif - elseブロックで10個の引数をとる点まで長くなります.テストケースが引数の数と共に指数関数的に成長するので、後はテスト不能になります.これを管理するには、次のような質問をします.
  • この関数はその名前を暗黙的に使用しますか?例えば、関数と呼ばれるかもしれませんcreateUser そして、コードでは、それだけでなく、ユーザーを作成するだけでなく、ウェルカムメールを送信します.その場合は、送信メールを別の関数に移動し、別々にテストする必要がありますcreateUserAndSendEmail したがって、他のPPLはこの抽象化レベルで機能の副作用を知っています.
  • ファイルをより小さく(もっと特定の)ファイルに分割するべきですか?仮定するとorder.go すべての順序関連の関数を扱うファイルは、手を取り出さないときに、このドメインのさまざまな側面に関係するファイルに分割しようとすることができます.例えば、私たちはorder_creator.go 具体的には注文作成手順、およびorder_history.go これは、ユーザーがいつでも検索するための履歴を保持し、おそらくorder_report.go 統計目的のファイルです.

  • データモデルを保護する
    モデルは、アプリケーションのコアにあり、あなたは常に互換性とトレーサビリティの理由のための歴史的なデータを運ばなければならないので、簡単にデータモデルを変更することができないことを覚えて、それはアプリケーションがそれらを蓄積すると、古いデータを移行するために、より長く、より長いかかります.また、コードの一部がまだ意識していないかもしれないそれらを消費するならば、データを編集/削除することは危険です.

    別のフィールドを追加する前に考えてください.
    いつか、新しい要件が入ってきたときにデータモデルに別のフィールドを追加するのは非常に簡単です.たとえば、あるモデルが保存されているときに、あるモデルが変更されたかどうかを知りたいなら、それを反映させるためにモデル上にブールフラグを追加するのは簡単ですが、潜在的な問題は3ヶ月後のことです.人々はもはやこのフィールドが何であるかを覚えていないかもしれません、そして、リファクタリングのとき、それは技術負債になります.代わりに、既存の状態から状態を引き出すことができればupdated_at このデータが変更されたかどうかを知るためにデータベースに保存する前にフィールド.

    他のモデルからコアビジネスモデルを保持
    DTOSは、関数の引数と戻り値の型のリファクタリングを使用して異なる目的のために変更される可能性があるため、いわゆるデータ転送モデル(DTOS)からのデータをモデル化して区別することは良い習慣です.しかし、我々が扱っているビジネスドメインが同じままである限り、コアデータモデルは同じままであるべきです.

    一貫して柔軟なインターフェイスを維持する
    もう一つの非常にチャレンジタスクAPIのほとんどは、クライアントの複数のバージョンをサポートするためには、IOSのアプリケーションは、アップルのレビューを通過し、新しいバージョンをリリースするには数日または数週間かかるかもしれないが、採用率は、徐々に古いバージョンを完全に殺す場合を除きます.したがって、ほとんどの場合、新しいスキーマを使用して古いAPIスキーマとデリバリー機能をサポートしなければなりません.我々が取ったアプローチは、Alwaysバージョンに我々のAPIのようです/api/v1/user /api/v2/user そして、要求と応答オブジェクトを別々に定義します(いくつかのPPL定義serializersもそうです).

    テスト
    現在の段階では、我々はまだすべてのリリースでの回帰を行うためにQAに依存しているとして、いくつかのUIテストでユニットテストとインテグレーション(API)テストに完全なカバレッジを持っています.あなたが私たちのような限られたエンジニアリングリソースが私たちのような限られたエンジニアリングリソースを持っているならば、完全なカバレッジE 2 Eテストを実行して、維持することが可能でないならば、単位テストとしての単位テストの品質に集中することは、プログラムの正しさについてだけではなく、より重要に、それが機能するべきである状況を説明する「文書」として機能します.これは、アプリケーションがより大きく、より複雑になるにつれて重要であるため、簡単に忘れると無視される多くのエッジのケースがあるでしょう.

    常にリファクタリング
    どのように慎重に我々はアプリケーションを構築するに関係なく、それは常にメシエを取得し、管理するのは難しいだけでなく、ビジネスロジックの蓄積だけでなく、チームが学習し、特定の問題を解決するための新しい方法を発見しているので、常に既存のコードを再訪問する時間を費やす必要があります.
    我々が非常におもしろいとわかる1つのものは、我々がエンジニアに彼らのPRSまたは提案をすべてのエンジニアに生のセッションでデモして、それの後ろで合理的であると説明するのを許すために金曜日の午後の数時間を常に割り当てるということです.

    結論.
    固体のソフトウェアを構築するには、いくつかの慣習ではなく、エンジニアリングカルチャーを必要とする、我々は常に小さなものに注意を払う必要がありますし、我々のアプローチを再考し、変更を行う場合でも、直接またはすぐに製品に利益を持っていない.