Skip to content

Repository files navigation

ArchView

ArchView is a tool for creating and inspecting Go program architecture based on annotations.

Introduction

To start using, add annotations like so:

package example

// architecture: Database
type DB interface {
	Query() string
}

// architecture: Service
type Users struct {
	db       DB
	comments *Comments
}

// architecture: Service
type Comments struct {
	db    DB
	users *Users
}

// architecture: Server
type Server struct {
	comments *Comments
	users    *Users
}

// we can create links between interfaces and implementations with idiomatic Go
var _ DB = &PostgresDB{}
var _ DB = (*SqliteDB)(nil)

// architecture: Database Implementation
type PostgresDB struct{}

func (*PostgresDB) Query() string { return "postgres" }

// architecture: Database Implementation
type SqliteDB struct{}

func (*SqliteDB) Query() string { return "sqlite" }

When we run the tool to output a graphviz dot file:

archview ./... | dot -Tsvg > graph.svg

Basic Diagram

We can also use more complicated tools, by outputting a graphml file:

archview -out example.graphml ./... 

With a tool like yEd we can try different ways of visualizing:

Prettier Diagram

Install

To install archview run:

go install github.com/storj/archview@latest

You'll also need a tool to layout the graph, for example:

  1. GraphViz for dot formats.
  2. yEd for graphml formats.

Running the tool outputs the graph definition:

archview [-format (dot|dot-basic|graphml|elk)] [-out <output file>] [packages]

Output and filtering

By default, ArchView writes DOT to standard output. -out graph.graphml selects GraphML from the file extension; an explicit -format takes precedence. dot includes styling and class clusters, dot-basic produces a minimal directed graph, graphml includes metadata and yEd labels, and elk produces ELK text.

Flag Behavior
-root example.Server Keep the named component and its transitive dependencies. Unknown names produce an error listing available components.
-skip-class Service Remove components of the given class and links to them. Applied after root selection.
-trim-prefix example. Shorten displayed names without merging distinct components.
-cluster class Group DOT nodes by class (the default). Use -cluster='' to disable grouping.
-nocolor Disable DOT coloring.

-root and -skip-class accept repeated flags or comma-separated values. Root names use fully qualified declaration names, before prefix trimming: use example.Pair for Pair[A, B any]. Full displayed type names are also accepted. Each -root value is matched literally first, so -root 'external:users,audit' selects that resource if it exists. Otherwise, the value is interpreted as a comma-separated list. Use repeated flags when selecting multiple names that contain commas, and quote names containing spaces. For example, with module path example:

archview -root example.Server -skip-class 'Database Implementation' -trim-prefix example. ./...
archview -cluster='' -nocolor ./... | dot -Tsvg > graph.svg
archview -format elk -out graph.elkt ./...

Package errors and invalid options are reported before the output file is opened. File output is written to a temporary file in the destination directory, synced, and closed before replacing the destination using a rename. Replacement is atomic on platforms that support atomic file renames. Existing file permissions are preserved; new files are private to the current user on Unix. Symlinks and other non-regular destinations are rejected. Standard output is streamed directly.

Dependency rules

Place // architecture: Class above a type declaration or an individual type inside a grouped declaration. A group annotation applies to its types unless a type has its own annotation.

Annotated types must be declared at package scope. Annotations on types inside functions, methods, closures, or nested blocks produce a source-location error; move those types to package scope so graph identifiers and links are unambiguous. Unannotated local types are allowed.

ArchView follows struct fields, including nested unannotated structs, aliases, multiple pointer levels, arrays, slices, channel elements, and both map keys and values. It also follows parameters and results of interface methods and function types, including callback fields and variadic parameters.

Generic instances link to the annotated generic declaration: a field of type *Cache[int] links to Cache[T]. Traversal stops when an annotated component is reached. Repeated references to the same target along the same field or method path produce one dependency link; separate fields remain separate links.

An annotated alias is a separate component: references to that alias link to it, while references to the original type keep linking to the original component. Unannotated aliases resolve to their targets. This also applies to generic aliases.

Explicit interface assignments such as var _ DB = (*PostgresDB)(nil) create implementation links. ArchView does not infer every possible interface implementation, analyze function bodies as call graphs, or inspect concrete methods merely because their receiver is annotated.

Assignments between identical interface types (including aliases) do not create implementation links. Repeated assertions for the same component pair produce one implementation link, independently of any ordinary dependency links.

Explicit relationships

Use a standalone line comment to describe a relationship that Go type references do not express:

// architecture:link Client -> Server : sends requests
// architecture:link Server -> example/worker.Worker : dispatches jobs

The syntax is // architecture:link Source -> Target with an optional : label. Separate the arrow and colon from endpoint names with spaces. Labels may contain multiple words. A bare type name refers to an annotated type in the comment's package; a qualified name uses the full import path followed by the type name. Use generic declaration names without type parameters, such as Cache.

Endpoints must be annotated types or declared external resources in the loaded packages. Forward references and cycles are supported. Identical explicit links are deduplicated; different labels and inferred dependencies remain separate. Malformed directives, unknown endpoints, and ambiguous names report source locations and stop output.

Explicit links participate in root selection and class filtering. DOT and GraphML include relationship labels (including yEd edge graphics); ELK retains the connections without labels. The yEd encoding follows the PolyLineEdge schema.

External resources and database tables

Declare resources that have no Go type with architecture:resource. Resource IDs start with external: and contain no whitespace. IDs are global across the loaded packages; a class may contain spaces. For example:

// architecture: Database Implementation
type Postgres struct{}

// architecture:resource external:users : Database Table
// architecture:resource external:comments : Database Table
// architecture:link Postgres -> external:users : reads and writes
// architecture:link Postgres -> external:comments : reads and writes
// architecture:link external:comments -> external:users : references

Directives may appear anywhere in a Go source file, including a dedicated architecture.go file. Resources can be either endpoint of a link, and do not require wrapper types, imports, or runtime code. Identical declarations share one resource; conflicting classes for the same ID are errors. Undeclared resources are errors, so a misspelled target cannot silently create a new node.

External resources appear in every output format, without Go documentation URLs. Use -root external:users to select a resource, or -skip-class 'Database Table' to hide tables. See the database/table example.

Clients and servers

Link a client to an annotated server by its full import path, or to a declared external service when the server's code is not loaded:

// architecture: Client
type Client struct{}

// architecture:link Client -> example/network/server.Server : sends requests
// architecture:resource external:payments-api : Remote Service
// architecture:link Client -> external:payments-api : HTTPS

An arrow expresses the direction you specify; no reverse link is inferred. Add Server -> example/network/client.Client : pushes events in the server's package as another architecture:link directive to show traffic in both directions.

The client/server example includes both kinds of endpoint, a reverse event link, and an unrelated external service. From that directory:

archview -root example/network/client.Client ./... | dot -Tsvg > network.svg
archview -root example/network/client.Client -skip-class 'Remote Service' ./...

The first command retains the client, server, and payments API. The second hides the remote API while preserving both links between the client and server.

About

Create architectural diagrams from Go code.

Resources

Stars

14 stars

Watchers

25 watching

Forks

Releases

Packages

Used by

Contributors

Languages