【发布时间】:2018-12-09 17:00:27
【问题描述】:
正确的软件架构是创建可维护项目的关键。正确的意思是100%主观的, 但最近我喜欢并尝试关注 Robert C. Martin(又名 Bob 叔叔)的 Clean Architecture。
虽然我真的很喜欢这个理论,但它缺乏一些实用的实施指南来解决开发人员可能面临的常见技术挑战。 例如,我一直在努力解决的一件事是正确实现演示者层。
演示者负责从我的用例中接受“响应”并以某种方式对其进行格式化 它可以“呈现”到我的输出设备(无论它是 Web 还是 CLI 应用程序)。
有多种方法可以解决这个问题,但它们通常属于以下类别之一:
- 演示者由用例本身通过某种输出接口调用
- 用例返回响应模型,控制器(最初称为用例)将此模型传递给演示者
选项 1 或多或少与 Clean Architecture/Uncle Bob 所说的相同(在书中和各种帖子中,见下文),选项 2 是一种可行的替代方法。
听起来很酷,但让我们看看如何在 Go 中实现它们。
这是我的第一个版本。为简单起见,我们的输出现在会发布到网络上。
另外,请原谅我的简短。
package my_domain
import "http"
type useCase struct {
presenter presenter
}
func (uc *useCase) doSomething(arg string) {
uc.presenter("success")
}
type presenter interface {
present(respone interface{})
}
type controller struct {
useCase useCase
}
func (c *controller) Action(rw http.ResponseWriter, req *http.Request) {
c.useCase("argument")
}
基本上它完全按照上面和清洁架构中的描述进行:有一个控制器调用用例(通过边界,此处不存在)。用例做了一些事情并调用了演示者(没有实现,但这正是问题所在)。
我们的下一步可能是实现 Presenter....但是考虑到 Go HTTP 处理程序中的输出是如何工作的,有一个很好的问题需要解决。即:请求范围。
每个请求都有它自己的响应写入器(传递给 http 处理程序),响应应该被写入其中。演示者没有可以访问的全局请求范围,它需要响应编写者。因此,如果我想遵循选项 1(调用演示者的用例),我必须以某种方式将它传递给以这种方式成为请求范围的演示者,而应用程序的其余部分是完全无状态的并且不是请求范围的,它们被实例化一次.
这也意味着我要么将响应编写器本身传递给用例和演示者(我宁愿不这样做),要么为每个请求创建一个新的演示者。
我在哪里可以这样做:
- 通过工厂在控制器中
- 在通过工厂的用例中(但话又说回来:用例必须接收响应编写器作为参数)
这带来了另一个问题:如果演示者是请求范围的,那么用例也是吗?
如果我想将演示者注入到用例结构中,那么可以,并且还必须在控制器中创建用例。
或者,我可以让演示者成为用例的参数(没有人说必须在“构造时间”注入依赖项)。但这仍然会在某种程度上将演示者与控制器耦合。
还有其他未解决的问题(例如,我应该在哪里发送 HTTP 标头),但这些问题不太具体。
这是一个理论问题,因为我还不确定我是否要使用这种模式,但我已经花了相当多的时间来思考这个问题,但到目前为止还没有找到完美的解决方案。
基于articles and questions 我已经阅读了有关该主题的内容:其他人也没有。
【问题讨论】:
标签: go architecture clean-architecture