Architecture 包含演示者或返回数据的用例?

Architecture 包含演示者或返回数据的用例?,architecture,clean-architecture,hexagonal-architecture,Architecture,Clean Architecture,Hexagonal Architecture,考虑到定义,尤其是描述控制器、用例交互者和演示者之间关系的小流程图,我不确定我是否正确理解“用例输出端口”应该是什么 干净的体系结构与端口/适配器体系结构一样,区分了主端口(方法)和辅助端口(由适配器实现的接口)。在通信流之后,我希望“用例输入端口”是一个主端口(因此,只是一个方法),“用例输出端口”是一个要实现的接口,可能是一个采用实际适配器的构造函数参数,以便交互者可以使用它 作为代码示例,这可能是控制器代码: Presenter presenter = new Presenter(); R

考虑到定义,尤其是描述控制器、用例交互者和演示者之间关系的小流程图,我不确定我是否正确理解“用例输出端口”应该是什么

干净的体系结构与端口/适配器体系结构一样,区分了主端口(方法)和辅助端口(由适配器实现的接口)。在通信流之后,我希望“用例输入端口”是一个主端口(因此,只是一个方法),“用例输出端口”是一个要实现的接口,可能是一个采用实际适配器的构造函数参数,以便交互者可以使用它

作为代码示例,这可能是控制器代码:

Presenter presenter = new Presenter();
Repository repository = new Repository();
UseCase useCase = new UseCase(presenter, repository);
useCase->doSomething();
演示者界面:

// Use Case Output Port
interface Presenter
{
    public void present(Data data);
}
最后,交互者本身:

class UseCase
{
    private Repository repository;
    private Presenter presenter;

    public UseCase(Repository repository, Presenter presenter)
    {
        this.repository = repository;
        this.presenter = presenter;
    }

    // Use Case Input Port
    public void doSomething()
    {
        Data data = this.repository.getData();
        this.presenter.present(data);
    }
}
上述图表本身似乎证实了这种解释,其中控制器和输入端口之间的关系由一个带有“尖锐”头部的实心箭头表示(UML表示“关联”,意思是“有一个”,其中控制器“有一个”用例),演示者和输出端口之间的关系由一个带有“白色”头部的实心箭头表示(UML表示“继承”,它不是“实现”的关系,但可能这就是它的含义)

然而,这种方法的问题是用例必须考虑演示本身。现在,我看到
Presenter
界面的目的是足够抽象,以表示几种不同类型的Presenter(GUI、Web、CLI等),它实际上只是表示“输出”,这是一个用例很可能具备的功能,但我仍然不完全相信它

现在,在Web上查找clean体系结构的应用程序时,我似乎只发现有人将输出端口解释为返回一些DTO的方法。这将类似于:

Repository repository = new Repository();
UseCase useCase = new UseCase(repository);
Data data = useCase.getData();
Presenter presenter = new Presenter();
presenter.present(data);

// I'm omitting the changes to the classes, which are fairly obvious
这很有吸引力,因为我们正在将“调用”表示的责任从用例中移开,所以用例不再关心如何处理数据,而只是提供数据。此外,在本例中,我们仍然没有违反依赖性规则,因为用例仍然不知道关于外层的任何信息

然而,用例不再控制实际演示的执行时间(这可能很有用,例如在那一刻做额外的工作,比如日志记录,或者在必要时完全中止)。另外,请注意,我们丢失了用例输入端口,因为现在控制器只使用
getData()
方法(这是我们新的输出端口)。此外,在我看来,我们打破了“告诉,不要问”的原则,因为我们要求交互者提供一些数据来处理它,而不是让交互者首先做实际的事情

那么,根据干净的体系结构,这两个备选方案中的任何一个都是用例输出端口的“正确”解释吗?它们都可行吗

在中,Robert Martin准确地描述了一个用例,在该用例中,交互者根据读取请求调用演示者。没有提到MVC、MVVC等,所以我猜Clean体系结构通常不能很好地与MVC配合使用

单击地图会导致调用PlacePincController。它收集点击的位置和任何其他上下文数据,构造placePinRequest数据结构并将其传递给PlacePinInteractor,后者检查pin的位置,如有必要进行验证,创建一个Place实体以记录pin,构造一个EditPlaceResponse对象并将其传递给EditPlacePresenter,后者将弹出PlaceEditor屏幕

一种可能的解释是,传统上进入控制器的应用程序逻辑,在这里被移动到交互器,因为我们不希望任何应用程序逻辑泄漏到应用程序层之外。因此,在这里,模型没有调用演示者,因为交互者不是模型,而是控制器的实际实现。模型只是传递的数据结构。这似乎得到了以下方面的证实:

该层中的软件是一组适配器,用于将数据从最适合用例和实体的格式转换为最适合某些外部机构(如数据库或Web)的格式


从原始文章开始,讨论接口适配器。由于控制器必须是一个将一种数据格式转换为另一种数据格式的瘦适配器,因此它不能包含任何应用程序逻辑,这些逻辑因此被移动到交互器。

文章说,用例独立于gui(演示者),因此控制器的工作是与用例(又名服务或工作流)和演示者对话

[更新2017-08-29]


如果模型使用演示者界面,这不再是一个干净的mvc、mvp或mvvm体系结构,而是其他东西。

我认为你已经很好地解释了你的问题和你对k3b答案的评论中的所有内容

关键的方面是:控制器和演示者是同一个类吗

如果您使用Asp.NETMVC作为web框架,例如controller和presenter是同一类。在这种情况下,在接口的意义上,不需要输出端口。控制器只调用交互器上的一个方法,并获取一些输出数据作为返回值

在控制器和演示者分开的情况下,需要有一种方法“将结果传递给演示者”。为此,需要输出端口。输出端口实际上是一个在用例圈中定义并在接口适配器圈中实现的接口

这两种方法都是可行的

如果您对更详细的示例感兴趣,可以查看我的博客系列:

更新:我又添加了一个
// in the controller for our console app
if (response.ErrorMessages.Count > 0)
{
    if (response.ErrorMessages.Contains(<invalid request>))
    {
        presenterForConsole.ShowError("...");
    }
    else if (response.ErrorMessages.Contains("another error")
    {
        presenterForConsole.ShowError("another error...");
    }
}
else
{
    presenterForConsole.Present(response);
}