Не заставляйте вашу модель выдавать бессмысленные сообщения об ошибках. Вы упускаете преимущества слоев абстракции.

Если вы какое-то время программировали на Go, вы знаете, что одним из его отличительных символов является обработка ошибок, к лучшему или к худшему. Некоторым людям это нравится, потому что это очень откровенно, некоторым людям это не нравится, потому что это так («Я хочу поймать это, а лучше разбираться с этим!»).

Этот пост не об этом, а о том, как обработка ошибок человеческим путем из глубины слоя модели может облегчить жизнь вам и пользователям.

Тот факт, что ваша модель взаимодействует с базой данных, не означает, что она должна игнорировать свою работу по созданию более личных и абстрактных ошибок. Мое хорошее практическое правило (по крайней мере, в Go): если функция или метод экспортируются, они должны возвращать изящные сообщения об ошибках.

Например, если в вашей модели это:

package model
type User struct {
    id int
    Name string
    Email string
}
func GetUserByEmail(email string) (*User, error) {
    var user User
    err := db.QueryRow("SELECT * FROM user_table").Scan(
        &user.id,
        &user.Name,
        &user.Email,
    )
    // This is bad
    if err != nil {
        return nil, err
    }
    return &user, nil
}

Тогда это плохо, потому что теперь вызывающий (из контроллера, маршрута или любого другого пакета), возможно, не может знать об ошибке и как ее обработать. Вы даже не узнаете, как выглядит эта ошибка, пока она не всплывет до вызывающей стороны, и вам, возможно, придется потратить время на отладку.

Будьте смелыми и решительными. Сделайте так, чтобы модель сама создавала ошибку, и решите, что это такое. Не упустите преимущества слоев абстракции. Сделай это:

if err != nil {
    return nil, fmt.Errorf("Error getting user %s\n", email)
}

Теперь вам будет легче найти ошибку. Вы можете распечатать исходную ошибку, чтобы понять ее причину.

Еще одна вещь, которую следует добавить: если ваши функции или методы экспортируются, не паникуйте и не сбой программы. Они предназначены для использования другими, так что будьте вежливы. Только log.Fatal внутри неэкспортированных. Фактически, большую часть времени вы хотите делать это только в управляемой среде, например, в функции main, где программа действительно запускается (и дает сбой). Другие слои не должны решать, запускать программу или прекращать работу.

Счастливого падения.