Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,29 @@
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

build:
env:
GOPATH: /home/runner/work/gopath
GOPATH: /home/runner/work/gopath
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22.2'

- name: setup gob
run: go install github.com/kcmvp/gob/cmd/gbc@latest
go-version: '1.24.0'

- name: Test with gob
run: gbc test
- name: Test
run: go test ./... -coverprofile=coverage.txt

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
23 changes: 0 additions & 23 deletions .golangci.yaml

This file was deleted.

252 changes: 170 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,172 @@
<p align="center">
Architecture Test Framework For Go Project
<br/>
<br/>
<a href="https://github.com/kcmvp/archunit/blob/main/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/kcmvp/archunit"/>
</a>
<a href="https://goreportcard.com/report/github.com/kcmvp/archunit">
<img src="https://goreportcard.com/badge/github.com/kcmvp/archunit"/>
</a>
<a href="https://pkg.go.dev/github.com/kcmvp/archunit">
<img src="https://pkg.go.dev/badge/github.com/kcmvp/archunit.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/kcmvp/archunit/blob/main/.github/workflows/build.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/kcmvp/archunit/build.yml?branch=main" alt="Build" />
</a>
<a href="https://app.codecov.io/gh/kcmvp/archunit" ref="nofollow">
<img src ="https://img.shields.io/codecov/c/github/kcmvp/archunit" alt="coverage"/>
</a>

</p>

## What is ArchUnit
ArchUnit is a simple and flexible extensible library for checking the architecture of Golang project.
with it, you can make your project's architecture visible, testable and stable by setting a set of predefined architectural rules.

## Why architecture test matters?
1. **Maintaining architectural integrity**: Architecture tests help ensure that the intended architectural design and principles are followed throughout the development process. They help prevent architectural decay and ensure that the system remains consistent and maintainable over time.
2. **Detecting architectural violations**: Architecture tests can identify violations of architectural rules and constraints. They help catch issues such as circular dependencies, improper layering, or violations of design patterns. By detecting these violations early, developers can address them before they become more difficult and costly to fix.
3. **Enforcing best practices**: Architecture tests can enforce best practices and coding standards. They can check for adherence to coding conventions, naming conventions, and other guidelines specific to the architectural style or framework being used. This helps maintain a consistent codebase and improves code quality.
4. **Supporting refactoring and evolution**: Architecture tests provide confidence when refactoring or making changes to the system. They act as a safety net, ensuring that the intended architectural structure is not compromised during the refactoring process. This allows developers to make changes with more confidence, knowing that they won't introduce unintended side effects.
5. **Facilitating collaboration**: Architecture tests serve as a form of documentation that communicates the intended architectural design to the development team. They provide a shared understanding of the system's structure and help facilitate collaboration among team members. Developers can refer to the tests to understand the architectural decisions and constraints in place.

## Features

- This project implements the principles of [Hexagonal architecture](https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)), which has been proven best practice of software architecture.You can easily apply rules with below aspects
- [Common Rules](#common-rules)
- [Lay Rules](#lay-rules)
- [Package Rules](#package-rules)
- [Type Rules](#type-rules)
- [Method Rules](#functionmethod-rules)
- [File Rules](#source-file-rules)
- Fully tested and easy to use, it can be used with any other popular go test frameworks.
- **NRTW(No Reinventing The Wheel)**. Keep using builtin golang toolchain at most.






## How to Use
1. Import the library
```go
go get github.com/kcmvp/archunit
```
2. Write a simple test
```go
func TestAllPackages(t *testing.T) {
pkgs := AllPackages().packages()
assert.Equal(t, 12, len(pkgs))
err := AllPackages().NameShouldBeSameAsFolder()
assert.NotNil(t, err)
# ArchUnit for Go

**A Go library for checking architecture, inspired by [ArchUnit](https://www.archunit.org/).**

`archunit` is a powerful and flexible library for enforcing architectural rules in your Go projects. It helps you maintain a clean and robust architecture by providing a fluent and declarative API for defining and validating architectural constraints. With `archunit`, you can ensure your code adheres to your intended design, preventing unwanted dependencies and maintaining modularity.

## Key Features

* **Declarative, Fluent API:** Define architectural rules in a clear and readable way.
* **Pre-defined Rules:** Get started quickly with a comprehensive set of rules for common Go best practices.
* **Custom Rules:** Easily create your own custom rules to enforce project-specific constraints.
* **Layered Architecture Validation:** Define and enforce dependencies between architectural layers.
* **Dependency Checking:** Prevent illegal dependencies between packages, types, and layers.
* **Naming Convention Enforcement:** Ensure your code follows consistent naming conventions.

## Installation

To install `archunit`, use `go get`:

```sh
go get github.com/kcmvp/archunit
```

## Getting Started

`archunit` makes it easy to get started. Here's a simple example of how to check for common Go best practices and enforce a basic layering rule.

Create a test file (e.g., `architecture_test.go`) in your project's root directory:

```go
package main_test

import (
"testing"

"github.com/kcmvp/archunit"
)

func TestArchitecture(t *testing.T) {
// Define your architectural layers
domainLayer := archunit.ArchLayer("Domain", "github.com/your-project/domain/...")
appLayer := archunit.ArchLayer("Application", "github.com/your-project/application/...")

// Initialize ArchUnit with your layers
arch := archunit.ArchUnit(domainLayer, appLayer)

// Define and validate your rules
err := arch.Validate(
// Use a pre-defined set of best practice rules
archunit.BestPractices(3, "config"),

// Define a custom rule: the domain layer should not depend on the application layer
archunit.Layers("Domain").ShouldNotRefer(archunit.Layers("Application")),
)

if err != nil {
t.Fatal(err)
}
}
```

## Core Concepts

`archunit` models your project's architecture using a set of core objects. You can select these objects and apply rules to them.

### Architectural Objects

* **Layer**: A logical group of packages defined by a path pattern (e.g., `.../domain/...`). Layers are the highest level of architectural abstraction.
* **Package**: A standard Go package.
* **Type**: A Go type, such as a `struct` or `interface`.
* **Function**: A Go function or a method on a type.
* **Variable**: A package-level variable.
* **File**: A Go source file (`.go`) or test file (`_test.go`).

### Building Blocks for Rules

To create architectural rules, you combine three main components:

* **Selections**: Allow you to choose specific architectural objects to apply rules to. You start a rule by selecting objects, for example, `Packages(HaveNameSuffix("service"))` or `Layers("Domain").Types()`.
* **Matchers**: Are used to filter selections based on their properties, like their name or package path. `archunit` provides built-in matchers like `WithName`, `HaveNamePrefix`, and `HaveNameSuffix`, which can be combined using `AnyOf` and `Not`.
* **Rules**: Define the constraints you want to enforce on a selection. Rules are chained to selections, for example, `ShouldNotRefer(...)`, `ShouldOnlyBeReferredBy(...)`, or `NameShould(...)`.

## Advanced Usage

### Layered Architecture

`archunit` is great for enforcing a layered architecture. Here's an example of how to define and validate a classic three-layer architecture:

```go
func TestLayeredArchitecture(t *testing.T) {
var (
domainLayer = archunit.ArchLayer("Domain", "github.com/my-project/domain/...")
appLayer = archunit.ArchLayer("Application", "github.com/my-project/application/...")
infraLayer = archunit.ArchLayer("Infrastructure", "github.com/my-project/infrastructure/...")
)

arch := archunit.ArchUnit(domainLayer, appLayer, infraLayer)

err := arch.Validate(
// The domain layer should not depend on any other layers
archunit.Layers("Domain").ShouldNotRefer(appLayer, infraLayer),

// The application layer should only depend on the domain layer
archunit.Layers("Application").ShouldOnlyRefer(domainLayer),

// The infrastructure layer can depend on the domain and application layers
archunit.Layers("Infrastructure").ShouldOnlyRefer(domainLayer, appLayer),
)

if err != nil {
t.Fatal(err)
}
}
```
> It's better to keep all the architecture tests in one file
## Rules
### Common Rules
1. PackageNameShouldBeSameAsFolderName
2. PackageNameShouldBe
3. SourceNameShouldBe
4. MethodsOfTypeShouldBeDefinedInSameFile
5. ConstantsShouldBeDefinedInOneFileByPackage
### Lay Rules
1. ShouldNotReferLayers
2. ShouldNotReferPackages
3. ShouldOnlyReferLayers
4. ShouldOnlyReferPackages
5. ShouldBeOnlyReferredByLayers
6. ShouldBeOnlyReferredByPackages
7. DepthShouldLessThan
### Package Rules
### Type Rules
### Function(Method) Rules
### Source File Rules

## Pre-defined Rules

`archunit` comes with a set of pre-defined rules for common Go best practices, available through the `BestPractices` function. These include checks for:

### Global Rules

These rules are applied to the entire project and are bundled together in the `BestPractices` function.

* `AtMostOneInitFuncPerPackage`: Ensures that each package has at most one `init` function.
* `ConfigurationFilesShouldBeInFolder`: Checks that all configuration files are in a specified folder.
* `ConstantsAndVariablesShouldBeGrouped`: Enforces that `const` and `var` declarations are grouped.
* `ConstantsShouldBeConsolidated`: Ensures all constants in a package are in a single file.
* `ContextShouldBeFirstParam`: Checks that `context.Context` is the first parameter in functions.
* `ContextKeysShouldBePrivateType`: Enforces that context keys are not built-in types.
* `ErrorShouldBeLastReturn`: Ensures that `error` is the last return value in functions.
* `NoPublicReAssignableVariables`: Prevents exported variables that can be reassigned.
* `NoUnusedPublicDeclarations`: Checks for public declarations that are not used outside their package.
* `PackageNamedAsFolder`: Enforces that a package's name matches its folder's name.
* `PackagesShouldNotExceedDepth`: Checks that package depth does not exceed a maximum.
* `TestDataShouldBeInTestDataFolder`: Ensures that test data is located in a `testdata` folder.
* `VariablesShouldBeUsedInDefiningFile`: Checks that variables are used in the file where they are defined.
* `VariablesAndConstantsShouldUseMixedCaps`: Enforces the `MixedCaps` naming convention.

### Architectural Object Specific Rules

These rules are applied to specific selections of architectural objects.

#### Dependency Rules (for Layers, Packages, and Types)

* `ShouldNotRefer`: Asserts that the selected objects do not refer to forbidden objects.
* `ShouldOnlyRefer`: Asserts that the selected objects only refer to allowed objects.
* `ShouldNotBeReferredBy`: Asserts that the selected objects are not referred to by forbidden objects.
* `ShouldOnlyBeReferredBy`: Asserts that the selected objects are only referred to by allowed objects.

#### Visibility and Location Rules (for Types, Functions, and Variables)

* `ShouldBeExported`: Asserts that the selected objects are exported.
* `ShouldNotBeExported`: Asserts that the selected objects are not exported.
* `ShouldResideInPackages`: Asserts that the selected objects reside in a package matching a given pattern.
* `ShouldResideInLayers`: Asserts that the selected objects reside in one of the given layers.

#### Naming Rules (for all Architectural Objects)

* `NameShould`: Asserts that the names of the selected objects match a given predicate.
* `NameShouldNot`: Asserts that the names of the selected objects do not match a given predicate.

## API Reference

For a complete API reference, see the [GoDoc](https://pkg.go.dev/github.com/kcmvp/archunit).

## Contributing

Contributions are welcome! Please feel free to submit a pull request or open an issue.

## License

`archunit` is licensed under the [MIT License](LICENSE).
45 changes: 45 additions & 0 deletions arch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package archunit

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestArchUnit(t *testing.T) {
// Define architectural layers

// Define a specific package for a more granular rule
utilsPackage := "github.com/kcmvp/archunit/internal/utils"
internalLayer := ArchLayer("Internal", "github.com/kcmvp/archunit/internal/...")
AppLayer := ArchLayer("App", "github.com/kcmvp/archunit")
// Execute the architectural validation
err := ArchUnit(internalLayer, AppLayer).Validate(

BestPractices(3, "config"),
Layers("App").ShouldNotBeReferredBy(Layers("Internal")),
// 2. Strict Dependencies: The App layer should ONLY depend on the Internal layer (and Go's standard library).
Layers("App").ShouldOnlyRefer(Layers("Internal")),

// 3. Encapsulation: The internal 'utils' package should not be used directly by the App layer.
Packages(WithName[Package](utilsPackage)).ShouldNotBeReferredBy(Layers("App")),

// 4. (AND/Not Example) Sample code (except the model) should not be used by the main App layer.
// This demonstrates combining matchers to create a precise selection.
Packages(
HaveNamePrefix[Package]("github.com/kcmvp/archunit/internal/sample"),
Not(WithName[Package]("github.com/kcmvp/archunit/internal/sample/model")),
).ShouldNotBeReferredBy(Layers("App")),

// 5. (AnyOf Example) The 'utils' package should not be depended on by services or repositories.
// This shows how to group multiple selections into a single rule.
Packages(WithName[Package](utilsPackage)).ShouldNotBeReferredBy(
Packages(AnyOf(
HaveNameSuffix[Package]("service"),
HaveNameSuffix[Package]("repository"),
)),
),
)

assert.NoError(t, err)
}
12 changes: 0 additions & 12 deletions doc.md

This file was deleted.

8 changes: 0 additions & 8 deletions docs/method.md

This file was deleted.

Loading
Loading