最近の記事

各クラスは独立して動作するようにする

Contents

Each class (struct + methods) should work independently. (各クラス(構造体+メソッド)は独立して動作するようにする)

解説

クラスが他のクラスやグローバル状態に暗黙的に依存していると、テストが困難になり、再利用性が低下します。各クラスは、必要な依存関係を明示的に受け取り、独立して動作すべきです。これにより、単体テストでモックやスタブを容易に注入でき、クラスの振る舞いを分離して検証できます。独立性の高いクラスは、異なるコンテキストでの再利用も容易です。

具体例

// 悪い例(グローバル状態に依存)
var globalDB *Database  // グローバル変数

type UserService struct{}

func (s *UserService) GetUser(id string) (User, error) {
    return globalDB.Query(id)  // 暗黙的な依存
}

// 良い例(依存関係を明示的に注入)
type UserService struct {
    db Database
}

func NewUserService(db Database) *UserService {
    return &UserService{db: db}
}

func (s *UserService) GetUser(id string) (User, error) {
    return s.db.Query(id)  // 明示的な依存
}

// テストでのモック使用
type MockDatabase struct{}

func (m *MockDatabase) Query(id string) (User, error) {
    return User{ID: id, Name: "Test User"}, nil
}

func TestGetUser(t *testing.T) {
    mockDB := &MockDatabase{}
    service := NewUserService(mockDB)

    user, err := service.GetUser("123")
    if err != nil {
        t.Fatal(err)
    }
    if user.Name != "Test User" {
        t.Errorf("unexpected user name: %s", user.Name)
    }
}

参考リンク

core coding-standard

略語ではなく完全な単語を使う

Contents

Use full words rather than abbreviations (e.g., compensatingTransaction instead of ct). Commonly accepted abbreviations like db are fine. (略語ではなく完全な単語を使う(例:ctではなくcompensatingTransaction)。dbのように一般的に受け入れられている略語は問題ない)

解説

略語は、書いた本人以外には意味が不明確で、コードの理解を妨げます。完全な単語を使用することで、コードの意図が明確になり、新しいメンバーでも容易に理解できます。現代のエディタには自動補完機能があるため、長い名前でも入力の手間は問題になりません。ただし、広く認知されている略語(例:db、api、html、url)は使用しても問題ありません。

具体例

// 悪い例(独自の略語を使用)
func ProcUsrData(usr *Usr) error {
    // usr, Procは何の略か不明
    res := CalcTtl(usr.Itms)
    // res, Ttl, Itmsは何の略か不明

    if err := SaveToDb(res); err != nil {
        return err
    }

    return nil
}

type Usr struct {
    Nm   string  // Nmは何の略?
    Em   string  // Emは何の略?
    Ph   string  // Phは何の略?
    Itms []Item
}

func CalcTtl(itms []Item) float64 {
    ttl := 0.0
    for _, itm := range itms {
        ttl += itm.Prc
    }
    return ttl
}

// 良い例(完全な単語を使用)
func ProcessUserData(user *User) error {
    total := CalculateTotal(user.Items)

    if err := SaveToDatabase(total); err != nil {
        return err
    }

    return nil
}

type User struct {
    Name  string
    Email string
    Phone string
    Items []Item
}

func CalculateTotal(items []Item) float64 {
    total := 0.0
    for _, item := range items {
        total += item.Price
    }
    return total
}

// 一般的な略語は使用可(良い例)
type UserRepository struct {
    db  *sql.DB     // dbは一般的
    api APIClient   // apiは一般的
}

func FetchFromAPI(url string) ([]byte, error) {
    // url, apiは一般的
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    return io.ReadAll(resp.Body)
}

func GenerateHTML(data TemplateData) string {
    // htmlは一般的
    return renderTemplate(data)
}

参考リンク

core coding-standard

構造体フィールドへのアクセスはゲッターで行いセッターは避ける

Contents

Access struct fields through getter methods; avoid setters. (構造体のフィールドへのアクセスはゲッターメソッドを通して行い、セッターは避ける)

解説

セッターメソッドを提供すると、オブジェクトの状態がいつでも変更可能になり、不変性が損なわれます。ゲッターのみを提供することで、オブジェクトの読み取り専用アクセスを実現し、予期しない状態変更を防げます。状態の変更が必要な場合は、ビジネスロジックを含むドメインメソッドとして実装すべきです。これにより、状態変更に対する制御が強化され、バグの混入を防ぎます。

具体例

// 悪い例(セッターが存在)
type Account struct {
    balance float64
}

func (a *Account) GetBalance() float64 {
    return a.balance
}

func (a *Account) SetBalance(amount float64) {
    a.balance = amount  // 検証なしで変更可能
}

func main() {
    acc := &Account{balance: 1000}
    acc.SetBalance(-500)  // 不正な状態を作成可能
}

// 良い例(ゲッターのみ、状態変更はドメインメソッド)
type Account struct {
    balance float64
}

func NewAccount(initialBalance float64) (*Account, error) {
    if initialBalance < 0 {
        return nil, errors.New("initial balance must be positive")
    }
    return &Account{balance: initialBalance}, nil
}

func (a *Account) Balance() float64 {
    return a.balance
}

func (a *Account) Deposit(amount float64) error {
    if amount <= 0 {
        return errors.New("deposit amount must be positive")
    }
    a.balance += amount
    return nil
}

func (a *Account) Withdraw(amount float64) error {
    if amount <= 0 {
        return errors.New("withdraw amount must be positive")
    }
    if a.balance < amount {
        return errors.New("insufficient balance")
    }
    a.balance -= amount
    return nil
}

参考リンク

core coding-standard

各関数の目的を1行コメントで要約する

Contents

Write a one-line comment summarizing the purpose of each function. Argument explanations are unnecessary except in special cases. (各関数の目的を要約する1行コメントを書く。引数の説明は特別な場合を除いて不要)

解説

関数の目的を簡潔に説明するコメントは、コードの理解を助ける重要な要素です。1行のコメントで「何をする関数か」を明確にすることで、コードレビューや保守作業が効率化されます。ただし、引数や戻り値の詳細な説明は、型や名前から自明な場合は不要です。コメントは実装の詳細ではなく、関数の意図や目的を伝えることに焦点を当てるべきです。

具体例

// 悪い例(コメントなし、または冗長)
func ProcessOrder(order Order) error {
    // この関数はOrderを受け取り、それを処理し、エラーがあればエラーを返します
    // 引数: order - 処理するOrder型の変数
    // 戻り値: error - エラーがあればerror、なければnil
    // ...
}

// 良い例
// ProcessOrder は注文を検証して保存し、確認メールを送信する
func ProcessOrder(order Order) error {
    // ...
}

// CalculateTax は商品価格に対する消費税額を計算する
func CalculateTax(price float64) float64 {
    return price * 0.1
}

// FetchActiveUsers はアクティブなユーザー一覧をデータベースから取得する
func FetchActiveUsers() ([]User, error) {
    // ...
}

// GenerateInvoicePDF は注文データからPDF形式の請求書を生成する
// pdfOptions: マージン、フォントサイズなど細かい制御が必要な場合のみコメント
func GenerateInvoicePDF(order Order, pdfOptions PDFOptions) ([]byte, error) {
    // ...
}

参考リンク

core coding-standard

依存性注入ではインターフェースに依存する

Contents

For dependency injection, depend on interfaces, not concrete implementations. (依存性注入では、具体的な実装ではなくインターフェースに依存する)

解説

依存性注入において、具体的な実装ではなくインターフェースに依存することで、疎結合な設計が実現されます。インターフェースに依存することで、実装の差し替えが容易になり、テスト時にモックやスタブを注入できます。この原則は依存性逆転の原則(DIP)の実践であり、変更に強く拡張しやすいコードベースを構築するための基盤となります。

具体例

// 悪い例(具体的な実装に依存)
type MySQLDatabase struct{}

func (db *MySQLDatabase) Query(sql string) []Row {
    // MySQL固有の実装
    return nil
}

type UserService struct {
    db *MySQLDatabase  // 具体的な実装に依存
}

// 良い例(インターフェースに依存)
type Database interface {
    Query(sql string) []Row
}

type MySQLDatabase struct{}

func (db *MySQLDatabase) Query(sql string) []Row {
    return nil
}

type UserService struct {
    db Database  // インターフェースに依存
}

func NewUserService(db Database) *UserService {
    return &UserService{db: db}
}

参考リンク

core coding-standard

ドメインの責務をユーティリティパッケージに入れない

Contents

Keep domain responsibilities out of utility packages. (ドメインの責務をユーティリティパッケージに入れない)

解説

ユーティリティパッケージは、文字列操作や日付計算など、ドメイン非依存の汎用的な機能を提供すべきです。ドメイン固有のビジネスロジックをユーティリティに配置すると、ドメイン層の概念が分散し、ビジネスロジックの理解が困難になります。ドメインロジックは、適切なエンティティやドメインサービスに配置することで、コードの意図が明確になり、保守性が向上します。

具体例

// 悪い例(ドメインロジックがutilに配置)
package util

func CalculateOrderTotal(items []Item) float64 {
    total := 0.0
    for _, item := range items {
        total += item.Price * float64(item.Quantity)
    }
    // 1万円以上で送料無料(ドメインルール)
    if total < 10000 {
        total += 500  // 送料
    }
    return total
}

// 良い例(ドメインロジックはドメイン層に配置)
package domain

type Order struct {
    items []Item
}

func (o *Order) CalculateTotal() float64 {
    subtotal := 0.0
    for _, item := range o.items {
        subtotal += item.Price * float64(item.Quantity)
    }

    shippingFee := o.calculateShippingFee(subtotal)
    return subtotal + shippingFee
}

func (o *Order) calculateShippingFee(subtotal float64) float64 {
    const freeShippingThreshold = 10000
    const standardShippingFee = 500

    if subtotal >= freeShippingThreshold {
        return 0
    }
    return standardShippingFee
}

// utilパッケージは汎用機能のみ
package util

func FormatCurrency(amount float64) string {
    return fmt.Sprintf("¥%.0f", amount)
}

参考リンク

core coding-standard

クラスはすべての依存関係を明示的に受け取る

Contents

A class must: 1. Explicitly receive all dependencies (via DI) — it should not secretly rely on any helper. 2. Maintain a consistent responsibility. (クラスは次を満たす必要がある:1. すべての依存関係を明示的に受け取る(DI経由)— ヘルパーに密かに依存してはいけない。2. 一貫した責任を維持する)

解説

依存関係が暗黙的だと、テストやデバッグが困難になり、コードの理解に時間がかかります。すべての依存関係をコンストラクタで明示的に注入することで、クラスが必要とするものが一目でわかり、モックやスタブを使ったテストが容易になります。また、一貫した責任を維持することで、クラスの目的が明確になり、変更の影響範囲が限定されます。これは保守性の高いコード設計の基本原則です。

具体例

// 悪い例(暗黙的な依存)
type OrderService struct {
    // 依存関係が明示されていない
}

func (s *OrderService) CreateOrder(items []Item) error {
    // 内部でグローバルなhelperを使用
    db := GetGlobalDB()  // 暗黙的な依存
    logger := GetGlobalLogger()  // 暗黙的な依存

    total := calculateTotal(items)
    if err := db.Insert(total); err != nil {
        logger.Error(err)
        return err
    }
    return nil
}

// 良い例(明示的な依存注入)
type OrderService struct {
    db     Database
    logger Logger
    pricer PriceCalculator
}

func NewOrderService(db Database, logger Logger, pricer PriceCalculator) *OrderService {
    return &OrderService{
        db:     db,
        logger: logger,
        pricer: pricer,
    }
}

func (s *OrderService) CreateOrder(items []Item) error {
    total := s.pricer.CalculateTotal(items)

    if err := s.db.Insert(total); err != nil {
        s.logger.Error(err)
        return err
    }

    return nil
}

参考リンク

core coding-standard

エンティティのドメイン検証は内部メソッドで実行する

Contents

When an Entity requires domain-specific validation, perform it inside the Entity’s own methods. (エンティティがドメイン固有の検証を必要とする場合、エンティティ自身のメソッド内で実行する)

解説

ドメイン層のエンティティは、自身のビジネスルールを理解し、検証する責任を持つべきです。検証ロジックをエンティティ外部に配置すると、ビジネスルールが分散し、変更時の整合性維持が困難になります。エンティティ内部にドメイン検証を配置することで、ビジネスロジックがカプセル化され、ルールの変更が局所化されます。これは、ドメイン駆動設計における重要な原則です。

具体例

// 悪い例(検証がエンティティ外部)
type Order struct {
    Amount float64
    Status string
}

func ValidateOrder(order Order) error {
    if order.Amount < 0 {
        return errors.New("amount must be positive")
    }
    if order.Status != "pending" && order.Status != "completed" {
        return errors.New("invalid status")
    }
    return nil
}

// 良い例(検証がエンティティ内部)
type Order struct {
    amount float64
    status string
}

func NewOrder(amount float64) (*Order, error) {
    o := &Order{amount: amount, status: "pending"}
    if err := o.validate(); err != nil {
        return nil, err
    }
    return o, nil
}

func (o *Order) validate() error {
    if o.amount < 0 {
        return errors.New("amount must be positive")
    }
    return nil
}

func (o *Order) Complete() error {
    if o.status != "pending" {
        return errors.New("only pending orders can be completed")
    }
    o.status = "completed"
    return nil
}

参考リンク

core coding-standard

インフラストラクチャはTerraformを使用して構築および運用する

ルール

Infrastructure is built and operated using Terraform

(インフラストラクチャはTerraformを使用して構築および運用する)

解説

このプロジェクトでは、インフラをIaCツールのTerraformで管理します。 コードでインフラを管理することは、生成AIによるインフラ構築を容易にするばかりか、GitとGithubを使ったトレーサビリティの確保の道も開きます。 今や手作業だけでAWSの管理をすることは想像もし難いことです。

サンプルコード

参考リンク

Infrastructure 開発ガイドライン Project Information

クラウドプラットフォームとしてAWSを使用する

ルール

AWS is used as the cloud platform

(クラウドプラットフォームとしてAWSを使用する)

解説

このプロジェクトでは、AWSを標準のクラウドプラットフォームとして採用しています。 AWSは手厚いサポートと情報提供、優秀なサービスラインナップ、安定したエコシステムを持ち、多くの企業での採用実績があります。

サンプルコード

参考リンク

Infrastructure 開発ガイドライン Project Information