应该从服务层返回什么类型的结果?

The*_*Sky 7 c# architecture domain-driven-design

比方说,我有一个PostsService创建帖子:

public class PostsService : IPostsService
{
    public bool Create(Post post)
    {
        if(!this.Validate(post))
        {
            return false;
        }

        try
        {
            this.repository.Add(post);
            this.repository.Save();
        }
        catch(Exception e)
        {
            return false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这样做的问题是,如果在存储库操作期间抛出异常,则会吞下它.Create()返回false,消费者知道的所有内容Post都是未添加,但不知道原因.

相反,我想到了一ServiceResult堂课:

public class ServiceResult
{
    public bool Success { get; private set; }
    public Exception Exception { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

这是一个很好的设计吗?或者我甚至需要向消费者报告这些错误?是否足以说"添加帖子时发生错误".然后在服务内部记录异常?

任何其他建议表示赞赏.

Jua*_*ano 5

I think it depends on the scope of why. Some failure reasons shouldn't concern the consumer, in my opinion and these should be logged in the service. On the other hand, there are cases when the consumer should be informed of these reasons, so in addition to logging them, you should indicate the cause of the error somehow.

For instance, suppose a very common use case of a user registration service. It's quite likely that you'd have a unique attribute that identifies the user (user handle, email, etc). This uniqueness must be enforced by the service (and probably at a database level too). Now, if the consumer is trying to register a user and it fails because the constrain was violated, the consumer should be notified of this reasons (so it could try some other user handle). Failed validations fall in the same scenario, most of the times.

If there's some kind of internal DB problem on the other hand, the consumer should not be informed of the details (because that's exclusive responsibility of the service; and it's not something the consumer can do anything about anyway).

考虑到这一点,我会说在许多情况下返回布尔值是不够的。所以拥有某种ServiceResult类型听起来并不是一个坏主意。我不确定我会包括Exception虽然。但也许您可以创建某种特定于您的服务的 ServiceExpection。有时错误代码也适用于此。


Geo*_*ker 5

捕获所有异常确实是个坏主意。你只能抓住那些你可以合理处理的东西。你无法处理所有异常,那么为什么要捕获它呢?为了防止堆栈跟踪?

如果你不希望你的用户看到可怕的堆栈跟踪,我明白,但你确实希望它死掉并且死得很可怕,这样某些东西就不会被损坏。

假设服务抛出了 OutOfMemoryException,您只是捕获了它,并且不知道您的应用程序是否像筛子一样泄漏内存或不正确地分配大对象。

这是一件坏事。

如果您不希望用户看到出了什么问题,那么您应该将捕获更改为:

catch (Exception e)
{
    //send it to yourself, log it. Just don't swallow it.
    LogErrorAndEmailDevTeam(e); 
    throw new SaveFailedException("We're sorry. \n" + 
    "It's not your fault, but something bad happened.\n\n" + 
    "We've been notified and will fix it as soon as possible.");
}
Run Code Online (Sandbox Code Playgroud)

但更好的办法是让应用程序惨死,而不是捕获该异常,这样您就可以在出现问题时快速知道。

您不希望您的应用程序继续处于损坏的状态,但事实就是如此catch (Exception)。你真的确定你能处理抛出的任何东西吗?

  • @Max 不,那是故意的。当开发团队打开电子邮件或通过日志收到警报时,他们会收到实际的异常;用户只是得到一个已知的异常,其中包含友好的错误消息。 (2认同)