Сопоставление внешних ошибок с ошибками домена в golang - PullRequest
0 голосов
/ 03 октября 2018

У меня есть тип сервиса с именем ComputeService, который реализует определенную доменную логику.Сам сервис зависит от реализации интерфейса Computer, который имеет метод Computer.Compute(args...) (value, error).Как показано, Compute само может возвращать определенные ошибки.

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

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

package arithmetic
type Computer struct {
}
func (ac Computer) Compute(args ....) (value, error) {
     // errors is a domain-errors package defined in compute service project
     return errors.NewDivideByZero()
}

ИЛИ

package compute
type Service struct {
}
func (svc Service) Process(args...) error {
    computer := findComputerImplementation(args...)
    val, err := computer.Compute(args...)
    if err != nil {
       if err == arith.ErrDivideByZero {
          // converting an arithmetic computer implementation 
          // specific error to domain error
          return errors.NewDivideByZero()
       } else if err == algebra.ErrInvalidCoEfficient {
          // converting an algebraic computer implementation 
          // specific error to domain error
          return errors.NewBadInput()
       }
       // some new implementation was used and we have no idea
       // what errors it could be returning. so we have to send
       // a internal server error equivalent here
       return errors.NewInternalError()
    }

}

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Чтобы сохранить контекст внутренней ошибки, просто вставьте исходную ошибку в ошибку домена

Это может использовать ошибки переноса, которые находятся на пути к Go 1.13 (Q4 2019), из выпуск 29934 , как подробно здесь .

err.Is () :

Как упоминает Расс Кокс :

Я думаю, мы все согласны с тем, что strings.Contains(err.Error(), "not found") - хрупкий код.

Надеюсь, мы также согласны с тем, что мы предпочли бы видеть код, подобный errors.Is(err, os.ErrNotExist).

Но дело в том, что во многих случаях для будущего развития пакета важно не давать вызывающим сторонам зависеть от конкретного результата ошибки, удовлетворяющего errors.Is(err, os.ErrNotExist), даже если это является основной причиной в результат сегодняшнего дня .
Это похоже на просмотр неэкспортированного поля или сравнение текста ошибки - это деталь, которая может измениться.

И хотя strings.Contains выглядит и хрупко, errors.Is не выглядит и не должно считаться хрупким.
Если мы хотим, чтобы оно не было хрупким, то нам нужно предоставить способ для пакетовсообщить подробности, не давая клиентам проверить его.Таким образом, ошибки не могут быть развернуты.

err.As () :

var pe *os.PathError
if errors.As(err, &pe) {
     use(pe)
}

% w :

func inner() error { return errors.New("inner error") }
func outer() error { return fmt.Errorf("outer error: %w", inner()) }

fmt.Fprintf("%+v", outer())
// outer error:
//     /path/to/file.go:123
//   - inner error:
//     /path/to/file.go:122

Текущий статус для Go 1.13 :

Просто констатирую то, что я вижу в качестве компромиссного решения, предложенного командой:

  • fmt.Errorf в настоящее время широко используется для переноса ошибок и возврата новой (непрозрачной) ошибки (поскольку вы не можете получить доступ к основной ошибке).
    '%w' теперь возможноиспользоваться для явного согласия на возврат ошибки, которую можно развернуть.
  • error разработан как базовый пакет без каких-либо зависимостей, поэтому каждый пакет может зависеть от него.
  • команда соглашаетсяразбираться в тех областях, в которых существуют широкие разногласия, и хотеть выпустить достаточно (ошибки. То есть, ошибки. Как расширение того, как большинство людей переносят ошибки), чтобы люди могли достигать целей.
  • Дженерики здесь нетпока, и мы не знаем, когда это произойдет: жаркое обсуждение этого вопроса приведет к обсуждению «значений ошибки 2».Похоже на детскую игру.
    errors.Is и errors.As чистые и достаточно лаконичные, чтобы в них можно было долго чувствовать себя комфортно.

Большинство спорных моментов были забиты до 1.14.

  • Wrapf не может жить с ошибками, так как это базовый пакет.
  • Wrapf означает, что команда ДОЛЖНА принять решение о том, что произойдет, когда передается ошибка nil: Punt on it.
  • Обтекание может противоречить идеям, рассматриваемым для локализации, интернационализации и т. Д.
  • ErrorFormatter и ErrorPrinter еще не получили более глубокого использования и имеют бородавки.Пунт.
0 голосов
/ 03 октября 2018

Разработчики Computer должны реагировать на ошибки домена, поскольку они наиболее близки к действию и лучше всего могут определить, что это за ошибка.Как вы сказали, наличие этой логики в ComputeService разрушает абстракцию.Если вам нужно сопоставить код с конкретными ошибками Computer с ошибками домена, создайте структуры-обертки, которые отделяют основную логику от этого кода упаковки ошибок.

Чтобы сохранить внутренний контекст ошибки, просто вставьте исходную ошибку в ошибку домена.и сделай IsSpecificDomainError помощников.

type MyDomainError struct {
    Err error
}

func NewMyDomainErr(err error) error {
    return &MyDomainError{err}
}

func IsMyDomainError(e error) bool {
    _, ok := err.(*MyDomainError)
    return ok
}
...