一个用例类可以有多个方法来处理业务逻辑?

问题描述 投票:0回答:1

考虑下面的 C# 示例,我在许多语言中看到了向系统注册用户的用例,通常我总是看到单个执行或调用函数来执行用例操作。

public class SignUp
{

    private readonly IRepository _repo;

    public SignUp(IRepository repo)
    {
        _repo = repo;
    }

    public User Execute(UserData data) 
    {
        return _repo.CreateUser(data);
    }

}

最近开始学习架构,买了一个在线课程,基本上都是以ASP.NET为例,如何通过http请求在控制器中调用用例,我思考:

用户可能已经存在,也可能发生错误,或者验证某些信息(例如电子邮件和密码)时可能出现错误,我必须将这些相同的信息传递回控制器,以便它可以传递正确的响应代码(500 、201、200、400...)。

像这样思考,我认为单个函数完成所有这些工作没有多大意义,因此,例如,如果我让它返回一个带有操作状态的枚举,并创建一个 GetError() 函数来获取操作过程中可能发生的错误或类似的错误可以吗,或者这是否违反了solid的单一责任原则?

我现在正在研究干净建筑

c# oop architecture software-design solid-principles
1个回答
0
投票

关于这个主题已经写了很多书,所以你不应该指望在这样的网站上可以回答这个问题。也就是说,这里有一些建议,希望不要太固执己见。

如果我们想象一种情况没有

SignUp
类,也许你有一个如下所示的控制器方法:

public ActionResult SignUp(UserData data)
{
    // Lots of complicated code, including:
    var user = _repo.CreateUser(data);
    // More complicated code goes here...

    return Ok(user);
}

如果将该代码的全部移到其他地方,则收效甚微。无论您将该代码移动到控制器上的私有帮助器方法,还是移动到单独的

Signup
类,都没有多大关系:

public class SignUp
{
    private readonly IRepository _repo;

    public SignUp(IRepository repo)
    {
        _repo = repo;
    }

    public ActionResult Execute(UserData data) 
    {
       // Lots of complicated code, including:
        var user = _repo.CreateUser(data);
        // More complicated code goes here...

        return OkObjectResult(user);
    }
}

这意味着您的控制器操作现在如下所示:

public ActionResult SignUp(UserData data)
{
    return new SignUp(_repo).CreateUser(data);
}

这样做有什么意义?

引入像

SignUp
这样的类的典型动机是分离关注点。请注意,如此处所示,假设的
SignUp
类具有多个职责。它做出业务决策,但它也处理 ASP.NET 特定的事务,这意味着它处理 HTTP 或用户界面问题。这体现在返回类型 (
ActionResult
) 以及
Ok
OkObjectResult
的使用中。

我们在这里看不到的另一个问题是

UserData
类是否使用 ASP.NET 特定的属性进行注释,例如 [Required][NotNull]

清洁架构的语言来说,这些东西就是细节,根据依赖倒置原则,细节应该依赖于抽象,而不是抽象依赖于细节。

因此,如果您想将抽象级别提高一个档次,您现在可以将这些关注点分开。一种方法是引入一个类似

SignUp
的类,它独立于 ASP.NET 处理用例:

public class SignUp
{
    private readonly IRepository _repo;

    public SignUp(IRepository repo)
    {
        _repo = repo;
    }

    public User Execute(UserData data) 
    {
       // Lots of complicated code, including:
        var user = _repo.CreateUser(data);
        // More complicated code goes here...

        return user;
    }
}

虽然仍然没有获得什么,但至少您现在已经将类与 ASP.NET 的任何知识解耦了。这使您有机会在新上下文中重用它,例如在单元测试中。 控制器操作现在如下所示:

public ActionResult SignUp(UserData data) { return Ok(new SignUp(_repo).CreateUser(data)); }

虽然收获还不是很多,但这应该概述了原理。您现在可以重复该过程:

您可能想要验证输入。同样,这可能是特定于应用程序的,因为 Web 应用程序的输入可能看起来与批处理作业的输入不同。因此,输入验证可以在

SignUp

类之外更好地建模。例如,回到关于

UserData
的评论,如果该类中有特定于 ASP.NET 的部分,则应在调用
SignUp.Execute
之前将其转换为另一种数据格式。
您可以继续分离这样的关注点,直到对代码进行有用的分解。

© www.soinside.com 2019 - 2024. All rights reserved.