Awesome
<img align="center" src="logo.png" alt="Arch-Go" title="Arch-Go" width="600px"/>
Arch-Go
Architecture checks for Go projects
Supported rules
Dependencies Checks
Supports defining import rules
- Allowed dependencies
- internal dependencies (same module)
- standard library dependencies
- external dependencies (3rd party packages)
- Not allowed dependencies
- internal dependencies (same module)
- standard library dependencies
- external dependencies (3rd party packages)
Package Content Checks
Allows you to define the contents of a set of packages, e.g. you can define that a desired package should only contain interfaces definitions. The supported checks are:
- shouldNotContainInterfaces
- shouldNotContainStructs
- shouldNotContainFunctions
- shouldNotContainMethods
- shouldOnlyContainInterfaces
- shouldOnlyContainStructs
- shouldOnlyContainFunctions
- shouldOnlyContainMethods
Function checks
Checks some functions properties, like the following:
- Maximum number of parameters
- Maximum number of return values
- Maximum number of public functions per file
- Maximum number of lines in the function body
Naming rules checks
Checks some naming rules, like the following:
- If a struct implements an interface that match some name pattern, then it's name should starts or ends with a specific pattern. For example, all structs that implements 'Verificator' interface, should have a name that ends with 'Verificator'
Configuration
File arch-go.yml
version: 1
threshold:
compliance: 100
coverage: 100
dependenciesRules:
- package: "**.impl.*"
shouldOnlyDependsOn:
internal:
- "**.foo.*"
- "*.bar.*"
shouldNotDependsOn:
internal: ["**.model.**"]
- package: "**.utils.**"
shouldOnlyDependsOn:
- "**.model.**"
- package: "**.foobar.**"
shouldOnlyDependsOn:
external:
- "gopkg.in/yaml.v3"
- package: "**.example.**"
shouldNotDependsOn:
external:
- "github.com/foobar/example-module"
contentsRules:
- package: "**.impl.model"
shouldNotContainInterfaces: true
- package: "**.impl.configuration"
shouldOnlyContainFunctions: true
- package: "**.impl.dependencies"
shouldNotContainStructs: true
shouldNotContainInterfaces: true
shouldNotContainMethods: true
shouldNotContainFunctions: true
functionsRules:
- package: "**.impl.**"
maxParameters: 3
maxReturnValues: 2
maxPublicFunctionPerFile: 1
maxLines: 50
namingRules:
- package: "**.arch-go.**"
interfaceImplementationNamingRule:
structsThatImplement: "*Connection"
shouldHaveSimpleNameEndingWith: "Connection"
Package name patterns
The package name can be defined as a fixed value or using * special character, to create a simple pattern.
Example | Description |
---|---|
*.name | Package should end with name and anything before, supporting only one level (for example foo/name, but no foo/bar/name) |
**.name | Package should end with name and anything before, supporting multiple levels (for example either foo/name and foo/bar/name) |
**.*name | Package should end with suffix name and anything before, supporting multiple levels (for example either foo/examplename and foo/bar/examplename) |
**.name* | Package should start with prefix name and anything before, supporting multiple levels (for example either foo/nameexample and foo/bar/nameexample) |
name.* | Package should start with name and anything before, supporting only one level (for example name/foo, but no name/foo/bar) |
name.** | Package should start with name and anything before, supporting multiple levels (for example either name/foo and name/foo/bar) |
**.name.** | Package should contain name, supporting multiple levels before and after (for example both foo/name/x/y/z, foo/bar/name and foo/bar/name/x) |
**.foo/bar.** | Package should contain foo/bar, supporting multiple levels before and after (for example both x/y/foo/bar/w/z, foo/bar/name and x/y/foo/bar) |
foo.**.bar | Package should start with foo, and ends with bar, and can have anything between them. (for example foo/bar, foo/test/blah/bar and foo/ok/bar) |
foo.*.bar | Package should start with foo, and ends with bar, and can have only one level between them. (for example foo/bar and foo/ok/bar, but no foo/test/blah/bar) |
Threshold configuration
Current version supports threshold configuration for compliance and coverage rate. By default both limits are set to 100%.
Compliance rate threshold
Represents how much the compliance level of the module is considering all the rules defined in the arch-go.yml
file. For example, if there are 4 rules and the module meets 3 of them, then its compliance level will be 75%.
Arch-Go will check that the compliance level of your module must be equals or greater than the compliance threshold defined in your arch-go.yml
file, if not then the verification will fail.
Coverage rate threshold
Represents how many packages in this module were evaluated by at least one rule.
- At this moment Arch-Go doesn't analize internal components as structs, interfaces, functions or methods, just verifies if a certain package complies with a rule package pattern, so then this rule is evaluated against this package
Arch-Go will check that the coverage level of your module must be equals or greater than the threshold defined in your arch-go.yml
file, if not then the verification will fail.
Usage
Using Arch-Go in command line
To install Arch-Go, run
$ go install -v github.com/arch-go/arch-go@latest
To execute this tool you have to be in the module path
$ cd [path-to-your-module]
Now you can execute Arch-Go tool
$ arch-go [flags]
Describing your architecture guidelines
Arch-Go includes a command to describe the architecture rules from arch-go.yml
file.
$ arch-go describe
The output of the describe
command is similar to:
$ arch-go describe
Dependency Rules
* Packages that match pattern '**.cmd.*',
* Should only depends on packages that matches:
- '**.arch-go.**'
Function Rules
* Packages that match pattern '**.arch-go.**' should comply with the following rules:
* Functions should not have more than 50 lines
* Functions should not have more than 4 parameters
* Functions should not have more than 2 return values
* Files should not have more than 5 public functions
Content Rules
* Packages that match pattern '**.impl.model' should not contain functions or methods
* Packages that match pattern '**.impl.config' should only contain functions
Naming Rules
* Packages that match pattern '**.arch-go.**' should comply with:
* Structs that implement interfaces matching name '*Verification' should have simple name ending with 'Verification'
Threshold Rules
* The module must comply with at least 100% of the rules described above.
* The rules described above must cover at least 100% of the packages in this module.
Time: 0.000 seconds
Supported flags
Flag | Description |
---|---|
--color | If not set (default: auto) in a tty the colors are printed. If set to yes or no the default is overridden. This can be useful in the CI. |
--verbose | Includes detailed information while the command is running. The shorthand is -v |
--html | Generates an HTML report with the evaluation result. The HTML report is generated in .arch-go/report.html |
--json | Generates a JSON report with the evaluation result. The JSON report is generated in .arch-go/report.json |
Examples
$ arch-go
$ arch-go -v
$ arch-go --verbose
$ arch-go --html
$ arch-go --json
$ arch-go --color
$ arch-go describe
Using Arch-Go programmatically
The current version of Arch-Go allows us to include architecture checks as part of the tests run by the go test tool.
You need to include Arch-Go as a dependency in your project, using
go get github.com/arch-go/arch-go@latest
Then you need to create Architecture Tests, there is an example of a simple test case:
package architecture_test
import (
"testing"
archgo "github.com/arch-go/arch-go/api"
config "github.com/arch-go/arch-go/api/configuration"
)
func TestArchitecture(t *testing.T) {
configuration := config.Config{
DependenciesRules: []*config.DependenciesRule{
{
Package: "**.cmd.**",
ShouldOnlyDependsOn: &config.Dependencies{
Internal: []string{
"**.cmd.**",
"**.internal.**",
},
},
},
},
}
moduleInfo := config.Load("github.com/arch-go/my-go-project")
result := archgo.CheckArchitecture(moduleInfo, configuration)
if !result.Pass {
t.Fatal("Project doesn't pass architecture tests")
}
}
The result
variable will store more than the verification result,
including details for each rule type and analyzed packages,
so then you can access all this data to create assertions as you need.
Contributions
Feel free to contribute.