I'm trying to build a project using the clean architecture, as described here. I found a great article on how to do this in Go.
The example is a very simple one, and the author puts their code into packages named based on the layer they are in. I like Uncle Bob's idea that an application's architecture should clearly communicate its intent. So I'd like my application to have top-level packages based on domain areas. So my file structure would look something like this:
/Customers
/domain.go
/interactor.go
/interface.go
/repository.go
/... the same for other domain areas
The problem with this is that multiple layers share the same package. So it's not all that clear when the dependency rule is being violated, because you don't have imports showing what depends on what.
I'm coming from a Python background, where this wouldn't be as much of an issue, because you can import individual files, so customers.interactor
could import customers.domain
.
We could achieve something similar in gO by nesting packages, so that the customers package contains a domain package and an interactor package, and so on. This feels clunky, and identically named packages can be annoying to deal with.
Another option would be to make multiple packages per domain area. One called customer_domain, one called customer_interactor, etc. But this feels dirty as well. It doesn't fit well with Go's package naming guidelines, and looks like all of these separate packages should somehow be grouped, since their names have a common prefix.
So what would be a good file layout for this?
Best Answer
There are a few solutions to this:
Each having their pros/cons.
Package Separation
This is the easiest way that doesn't require building anything extra. It comes in two flavors:
Or:
This however breaks the wholeness of the concept of
User
. To understand or modifyUser
you need to touch several packages.But, it does have a good property that it's more obvious when
model
importscontroller
, and to some extents it's enforced by language semantics.Review Analysis
If the application isn't large (less than 30KLOC) and you have good programmers, it's usually not necessary to build anything. Organizing structures based on value will be sufficient, e.g.:
Often the "constraint violations" are of little significance or are easy to fix. It harms clarity and understandability -- as long as you don't let it get out of hand, you don't have to worry about it.
Static/Runtime Analysis
You can also use static or runtime analysis to find these faults, via annotations:
Static:
Dynamic:
Both static/dynamic can also be done via fields:
Of course the lookup for such things become more complicated.
Other versions
Such approaches can be used in elsewhere, not just type constraints, but also func naming, API-s etc.
https://play.golang.org/p/4bCOV3tYz7