douyi7055 2018-07-01 01:13
浏览 124

如何根据Clean Architecture在Golang中实现演示者?

Proper software architecture is key to create a project that is maintainable. What proper means is 100% subjective, but lately I like and try to follow Clean Architecture by Robert C. Martin (aka Uncle Bob).

Although I really like the theory, it lacks some sort of practical implementation guide for common technical challenges developers may face. One of the things I've been struggling with for example is properly implementing the presenter layer.

The presenter is responsible for accepting the "response" from my use case and formatting it in a way that it can be "presented" to my output device (regardless if it is a web or a CLI application).

There are multiple approaches for this problem, but they usually fall under one of these categories:

  1. The presenter is called by the use case itself through some sort of output interface
  2. The use case returns the response model and the controller (which originally called the use case) passes this model to the presenter

Option 1 is more or less the same as what Clean Architecture/Uncle Bob says (in the book and in various posts, see later), Option 2 is rather an alternative approach which works.

Sounds cool, but let's see how we can implement them in Go.

Here is my first version. For simplicity, our output goes to the web now.

Also, please excuse my brevity.

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")
}

Basically it does exactly as described above and in Clean Architecture: There is a controller which calls a use case (through a boundary, which is not present here). The use case does something and calls the presenter (which is not implemented, but it's exactly the question).

Our next step could be implementing the presenter....but given how output works in Go HTTP handlers there is a nice problem to solve. Namely: request scope.

Every request has it's own response writer (passed to the http handler) where the response should be written. There is no global request scope that can be accessed by the presenter, it needs the response writer. So if I want to follow option 1 (use case calling the presenter), I have to pass it somehow to the presenter which becomes request scoped this way, while the rest of the application is completely stateless and not request scoped, they are instantiated once.

That also means that I either pass the response writer itself to the use case and the presenter (and I would rather not do that) or create a new presenter for each request.

Where can I do that:

  1. In the controller via factories
  2. In the use case via factories (but then again: the use case would have to receive the response writer as a parameter)

This bring in another problem: if the presenter is request scoped, is the use case too?

If I want to inject the presenter into the use case struct, then yes it is and the use case has to be created in the controller as well.

Alternatively I can make the presenter a parameter of the use case (noone said a dependency must be injected at "construction time"). But that would still somewhat couple the presenter to the controller.

There are other, unanswered issues (like where should I send HTTP headers for example), but those are less Go specific.

This is a theoretical question as I'm not yet sure that I want to use this pattern, but I've spent quite an amount of time thinking about this problem without finding the perfect one so far.

Based on the articles and questions I've read about the topic: others haven't either.

  • 写回答

1条回答 默认 最新

  • du1462 2018-07-01 10:38
    关注

    I can tell you about my experience according to Clean Architecture. I spent time on that issue, reading articles and testing code. So I'd like to suggest you the following post and the source code attached, it helped me a lot:

    It's a really good starting point, I'm designing my software in that way developing restful web app up to the presentation to the user passing through jQuery and Bootstrap. I can claim that now my software is really diveded into indipendent layers. Also it helped me to undestand the power of te golang interfaces and finally make simple testing each part of software. Hope this help you too.

    评论

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度