Configuring the architecture analysis
Configuring architecture analysis using YAML or JSON files with groups and perspectives, nested groups, and constraints.
The configuration file
How it models the architecture
The architecture analysis in Sonar is configured via a YAML or JSON file. It has two functions:
To declare the formal architecture of the codebase using Groups and Perspectives
To declare architectural Constraints that enforce the formal architecture
A Perspective is a structured view of your codebase, defining how parts of your code are organized into architectural elements, called Groups. Groups can be nested, forming a hierarchy that reflects your domain concepts. A project can have multiple Perspectives, each offering a different view of the architecture. For example, one Perspective might illustrate architectural layers, while another maps features to the relevant parts of the code. See "Groups and Perspectives" below for more information.
A Constraint is a rule your team defines and enforces through Sonar. Constraints are declared in the architecture file, and Sonar verifies them in CI/CD, raising issues when divergences occur.
There are two types of constraints:
Group constraints - They are defined within a Perspective and apply to hierarchical groups.
Top-Level constraints - They apply to the entire codebase and use raw code patterns like globs and wildcards
See the "Constraints" section below for more information.
Using a configuration file and format
The configuration file path can be specified via thesonar.architecture.configpath property. The following example runs the analysis in a maven project using the configuration file myArchitecture.json in the project root directory:
mvn clean verify sonar:sonar -Dsonar.architecture.configpath=./myArchitecture.jsonIt must be provided either in JSON or YAML format; .json, .yaml and .yml are valid file name extensions.
If no configuration file is being specified, the analyzer looks for a file architecture.json, architecture.yaml or architecture.yml in the project root directory by default. If you want no configuration file to be used at all, you can set the following property for the analysis:
mvn clean verify sonar:sonar -Dsonar.architecture.noconfigNote that this does not disable the architecture analysis. It just makes features unavailable that need configuration, but features that don’t need configuration (e.g., cycle detection) will still work.
All configuration properties
This section lists all properties available in the architecture configuration file. Refer to the ongoing sections for more information and configuration examples.
Configuration file:
perspectives(optional) – An array ofPERSPECTIVE. These are the perspective declarations that describe the formal architecture of the codebase. See "Groups and Perspectives" below.constraints(optional) – An array ofTOP-LEVEL-CONSTRAINT. These are the top-level constraints not tied to an individual perspective. See "Top-Level Constraints Declaration" below.
PERSPECTIVE
label(required) – Unique identifier for the perspective, which can contain any character except/.description(optional) – Provides more information about the perspective in human-readable form.groups(optional) – An array ofGROUP. A perspective can declare one or many groups. These are the structural elements that make up the perspective. While the property is optional, a perspective without groups would usually be useless.constraints(optional) – An array ofPERSPECTIVE-CONSTRAINT. These are the constraints declared for this perspective. See "Perspective Constraints Declaration" below.
GROUP
label(required) – Unique identifier for the group within its parent group or perspective. It can contain any character except/.description(optional) – Provides more information about the group in human-readable form.patterns(required) – Specifies the code elements that are contained in the group in the same way as patterns are used in Constraints. See the "Constraints" section below for more information.groups(optional) – An array ofGROUP. A group can declare one or many subgroups, allowing for the nested declaration of groups.
PERSPECTIVE-CONSTRAINT
from(required) – Patterns for the elements that use thetoelements in this constraint. This is an array of strings, each one representing a group path. The pattern can contain globs or regular expressions. See "Perspective Constraints Declaration" and "Wildcards" below.to(required) – Patterns for the elements used by thefromelements in this constraint. This is an array of strings, each one representing a file path pattern. The pattern can contain globs or regular expressions. See "Perspective Constraints Declaration" and "Wildcards" below.message(optional, or stand-alone) – Issue message specific for this constraint if used together withfromandto, or global issue message if used stand-alone. See "Custom Issue Messages" below.relation(optional) – Indicates whether this is a constraint that allows or denies the access fromfromtotoelements. Possible values aredenyandexclusive-allow. The constraint type defaults todenyif this property is not specified. See "Constraint Types" below.
TOP-LEVEL-CONSTRAINT
from(required) – Patterns for the elements that use thetoelements in this constraint. This is an array of strings, each one representing a file path pattern. The pattern can contain globs or regular expressions. See "File path and name pattern" and "Wildcards" below.to(required) – Patterns for the elements used by thefromelements in this constraint. This is an array of strings, each one representing a file path pattern. The pattern can contain globs or regular expressions. See "File path and name pattern" and "Wildcards" below.message(optional, or stand-alone) – Issue message specific for this constraint if used together withfromandto, or global issue message if used stand-alone. See "Custom issue messages" below.relation(optional) – Indicates whether this is a constraint that allows or denies the access fromfromtotoelements. Possible values aredenyandexclusive-allow. The constraint type defaults todenyif this property is not specified. See "Constraint Types" below.
JSON schema
When working on an architecture configuration file in the IDE or text editor of your choice, we recommend using this JSON schema. It provides validation and autocompletion features, thereby reducing the risk of errors in your configuration.
Groups and Perspectives
Declaration
The architecture configuration file can model the different views on the codebase using Groups and Perspectives. While a perspective represents a specific view, it consists of several groups that represent structural units within that perspective. Perspectives can overlap, include the entire codebase, or comprise just a subset of it. This means that an individual code element can be covered by one or many perspectives, or by no perspective at all.
Under the array property perspectives, you can specify one or many perspectives. A perspective has the following properties:
label(required) – Unique identifier for the perspective, which can contain any character except/.description(optional) – Provides more information about the perspective in human-readable form.groups(optional) – A perspective can declare one or many groups. These are the structural elements that make up the perspective. While the property is optional, a perspective without groups would usually be useless.
Within the groups property of a perspective or group, you can specify one or many groups. A group has the following properties:
label(required) – Unique identifier for the group within its parent group or perspective. It can contain any character except/.description(optional) – Provides more information about the group in human-readable form.patterns(required) – Specifies the code elements that are contained in the group in the same way as patterns are used in Constraints. See the "Constraints" section below for more information.groups(optional) – A group can declare one or many subgroups, allowing for the nested declaration of groups.
The following example models the architectural layers of a codebase:
{
"perspectives": [
{
"label": "Layers",
"description": "Application Layers",
"groups": [
{
"label": "UI Layer",
"patterns": ["src/*/com/example/ui/**"]
},
{
"label": "Service Layer",
"patterns": ["src/*/com/example/services/**"]
},
{
"label": "Data Layer",
"patterns": [
"src/*/com/example/repos/**",
"src/*/com/example/dtos/**"
]
}
]
}
]
}Multiple perspectives
The following example adds a Modules perspective (which is an orthogonal view to the layers perspective) and a Main/Test perspective to the previous example. Each of the perspectives covers the entire codebase.
{
"perspectives": [
{
"label": "Layers",
"description": "Application Layers",
"groups": [
{
"label": "UI Layer",
"patterns": ["src/*/com/example/ui/**"]
},
{
"label": "Service Layer",
"patterns": ["src/*/com/example/services/**"]
},
{
"label": "Data Layer",
"patterns": [
"src/*/com/example/repos/**",
"src/*/com/example/dtos/**"
]
}
]
},
{
"label": "Modules",
"description": "Application Functional Units",
"groups": [
{
"label": "Customers",
"patterns": ["src/*/com/example/*/customers/**"]
},
{
"label": "Products",
"patterns": ["src/*/com/example/*/products/**"]
},
{
"label": "Events",
"patterns": ["src/*/com/example/*/events/**"]
}
]
},
{
"label": "Main/Test",
"description": "Main and Test Code",
"groups": [
{
"label": "Main",
"description": "Main Code",
"patterns": ["src/main/**"]
},
{
"label": "Test",
"description": "Test Code",
"patterns": ["src/test/**"]
}
]
}
]Nested groups
Declaration
Groups can declare subgroups, which have the same features as top-level groups. This allows for defining architectures with multiple structural levels, rather than just flat architectures. The following example declares a perspective that distinguishes between modules on the first level and main/test code on the second.
{
"perspectives": [
{
"label": "Modules",
"groups": [
{
"label": "Customers",
"patterns": ["src/*/com/example/*/customers/**"],
"groups": [
{
"label": "Main",
"patterns": ["src/main/**"]
},
{
"label": "Test",
"patterns": ["src/test/**"]
}
]
},
{
"label": "Products",
"patterns": ["src/*/com/example/*/products/**"],
"groups": [
{
"label": "Main",
"patterns": ["src/main/**"]
},
{
"label": "Test",
"patterns": ["src/test/**"]
}
]
},
{
"label": "Events",
"patterns": ["src/*/com/example/*/events/**"],
"groups": [
{
"label": "Main",
"patterns": ["src/main/**"]
},
{
"label": "Test",
"patterns": ["src/test/**"]
}
]
}
]
}
]
}Label reuse
A group label must be unique within its parent group or perspective. Outside the enclosing declaration, it can be reused. This is demonstrated in the above example, where the labels Main and Test are being reused multiple times.
Patterns intersection
For an element to be covered by a subgroup, it must not only match the patterns of that group, but the patterns of its parent groups as well. Formally, the matcher algorithm traverses up the path from a subgroup through all its parent groups, and a code element is considered a path of the subgroup only if it matches all the patterns along that path.
This simplifies the usage of subgroup patterns, because you do not need to specify the intersection pattern explicitly. Take this excerpt from the above example:
{
"perspectives": [
{
"label": "Modules",
"groups": [
{
"label": "Customers",
"patterns": ["src/*/com/example/*/customers/**"],
...
"groups": [
{
"label": "Main",
"patterns": ["src/main/**"]
}
]
},
...
]
}
]
}An element will only be part of the subgroup Main in group Modules if it is located under src/main/com/example/customers/". However, we don’t need to be that specific. To declare the pattern src/main/*is sufficient to achieve the same effect. This is a trivial example, but note that patterns can become arbitrarily complex.
Constraints
Top-Level Constraints Declaration
Top-level constraints define which files and folders are allowed or denied access to each other. A constraint is made up of two required properties from and to that declare from which file or name pattern to which file or name pattern the constraint is applied. By default, constraints are deny constraints, which means that elements matching the from pattern are not allowed to use elements matching the to pattern in any way, such as importing them, or using classes or members from them.
The example configuration file in JSON format below shows the declaration of a simple top-level constraint:
{
"constraints": [
{
"from": ["com/example/ui/**"],
"to": [
"com/example/repos/**",
"com/example/dtos/**"
]
}
]
}The YAML equivalent looks like this:
constraints:
- from:
- "com/example/ui/**"
to:
- "com/example/repos/**"
- "com/example/dtos/**"The declared constraint prevents any source code element located under the path:
com/example/uifrom using any source code element located under the paths:
com/example/repos
com/example/dtosThe following example declares two top-level constraints:
{
"constraints": [
{
"from": ["com/example/ui/**"],
"to": [
"com/example/repos/**",
"com/example/dtos/**"
]
},
{
"from": ["com/example/services/**"],
"to": ["com/example/dtos/**"]
}
]
}Perspective Constraints Declaration
Perspective constraints work like top-level constraints in all aspects, except for the patterns used in the from and to properties. Instead of locating source code elements by file path or fully qualified name (see the "File path and name pattern" section below for more information), perspective constraints use paths to groups or subgroups within the declared groups. For example, if a top-level group Customer contains a subgroup UI, which contains a subgroup Test, then Customer/UI/Test is the path to that subgroup.
Perspective constraints are applied between groups and subgroups, not individual files. Wildcards are still supported in perspective constraints, allowing for flexible pattern matching. For instance, the pattern */UI/Test selects the subgroup Test in the subgroup UI contained in any top-level group.
The following example adds a constraint to the example from the "Declaration" section below that forbids access from main code to test code for the top level group Customers:
{
"perspectives": [
{
...
"constraints": [
{
"from": ["Customers/Main"],
"to": ["Customers/Test"]
}
]
}
]
}The following example adds a constraint that forbids access from main code to test code for all top level groups:
{
"perspectives": [
{
...
"constraints": [
{
"from": ["*/Main"],
"to": ["*/Test"]
}
]
}
]
}Constraint Types
The constraint type can be specified with the relation property. There are two types of constraints:
deny- Access to thetoelements by any of thefromelements is denied.exclusive-allow- Access to thetoelements is allowed only by thefromelements, by thetoelements themselves, and no others.
If not declared, the constraint type defaults to deny.
The following example states that elements in com/example/dtos can only be used by themselves and by elements in and com/example/repos.
{
"constraints": [
{
"from": ["com/example/repos/**"],
"to": ["com/example/dtos/**"],
"relation": "exclusive-allow"
}
]
}Custom Issue Messages
By default, constraint violations are reported in a generic form:
$from should not reference $to due to architectural constraints.
Here, $from and $to represent the patterns of the constraint being violated.
To provide more context, architects can define custom issue messages. These messages help developers understand which constraint was violated and the rationale behind it, thereby enhancing their comprehension of the architecture and design principles of the codebase.
Global issue messages
Custom issue messages can be declared globally, applying to a group of constraints, or locally for a specific constraint. A global issue message is declared in place of a constraint, like in the following example:
{
"constraints": [
{
"message": "Lower application layers should not depend on higher ones"
}, {
"from": ["com/example/dtos/**"],
"to": ["com/example/repos/**"]
}, {
"from": ["com/example/repos/**"],
"to": ["com/example/services/**"]
}
]
}This message applies to all subsequent constraints – unless overridden by a constraint-specific message – until the next message is declared. In the following example shows two different messages applied to subsequent constraints:
{
"constraints": [
{
"message": "Lower application layers should not depend on higher ones"
}, {
"from": ["com/example/dtos/**"],
"to": ["com/example/repos/**"]
}, {
"from": ["com/example/repos/**"],
"to": ["com/example/services/**"]
}, {
"message": "These modules should not depend on each other"
}, {
"from": ["com/example/*/Customers"],
"to": ["com/example/*/Products"]
},
...
]
}Resetting the global message
To reset the global issue message and revert to the default format:
$from should not reference $to due to architectural constraints.
Specify an empty message string. In this example, the custom message applies only to the first two constraints, while the ongoing constraints revert to the default message:
{
"constraints": [
{
"message": "Lower application layers should not depend on higher ones"
}, {
"from": ["com/example/dtos/**"],
"to": ["com/example/repos/**"]
}, {
"from": ["com/example/repos/**"],
"to": ["com/example/services/**"]
}, {
"message": ""
}, {
"from": ["com/example/*/Customers"],
"to": ["com/example/*/Products"]
},
...
]
}Using "from" and "to" pattern in custom issue messages
Custom issue messages can incorporate the $from and $to pattern, similar to the default message:
$from should not reference $to due to architectural constraints.
This allows for dynamic insertion of the constraints pattern being violated, as demonstrated in the following example:
{
"constraints": [
{
"message": "$from module should not depend on $to module"
}, {
"from": ["com/example/*/Customers"],
"to": ["com/example/*/Products"]
},
...
]
}Constraint-specific issue messages
Constraint-specific issue messages override global ones. They are declared as a property of the respective constraint, such as in the following example:
{
"constraints": [
{
"message": "Lower application layers should not depend on higher ones"
}, {
"message": "DTOs must not depend on data repositories",
"from": ["com/example/dtos/**"],
"to": ["com/example/repos/**"]
}, {
"from": ["com/example/repos/**"],
"to": ["com/example/services/**"]
}
]
}Exceptions from Constraints
If not regulated by a constraint, which means that there is no constraint whose from and to pattern matches the elements, then the access between two elements is allowed. If multiple constraints match, they are applied in the order in which they appear in the configuration file to determine the accessibility between elements. This allows to specify an exception for a deny-constraint or allow-constraint.
In the following example, elements from com/example/ui cannot access elements from com/example/repos, except if they are located in com/example/ui/internal.
{
"constraints": [
{
"from": ["com/example/ui/**"],
"to": ["com/example/repos/**"],
"relation": "deny"
},
{
"from": ["com/example/ui/internal/**"],
"to": ["com/example/repos/**"],
"relation": "exclusive-allow"
}
]
}File path and name pattern
Currently, constraints can only use file-path patterns. They provide a language-agnostic method to locate source code elements in the file system.
Future versions of the configuration schema will also support fully qualified name patterns. These are more natural to the developer of a language, but they are not language agnostic, as different languages have different concepts for their visibility scopes. For example, in Java, the scope of the package foo.bar is not a subscope of the package foo, whereas in C#, the namespace foo.bar is a subscope of foo.
Consider the following example of a constraints declaration for a Java project. This is an invalid example as the constraint patterns use fully qualified names, not file paths. The resulting constraint would never match.
{
"constraints": [
{
"from": ["com.example.ui.*"],
"to": ["com.example.repos.*"]
}
]
}Wildcards
Constraint patterns can contain wildcards. Both glob patterns and regular expressions are supported.
Globs
Globs are less powerful than regular expressions but are sufficient for most scenarios and are easier to use. By default, all patterns in the architecture configuration file are globs, while regular expressions are identified by a special marker.
Glob patterns in the architecture configuration file support the following features:
?- Any single character*- Any sequence of zero or more characters that does not cross path separator boundaries**- Any sequence of zero or more characters that can cross path separator boundaries[abc]- Any of the charactersa,b,c[^abc]- Any character excepta,b,c
The path separator depends on the path type. For file paths, the separator is /, meaning that foo/*/baz matches foo/bar/baz but not foo/baz or foo/bar/bas/baz. For Java namespace paths, the separator is ., although currently only file paths are supported (see "File path and name pattern" above).
The following example prevents any .java file that doesn’t start with letter X, located under any ui folder from using any element located under any repos folder:
{
"constraints": [
{
"from": ["**/ui/**/[^X]*.java"],
"to": ["**/repos/**"]
}
]
}Regular Expressions
Patterns that start with ^ and end with $ are interpreted as regular expressions. They support all features of Java Regular Expressions. The following example is equivalent to the above, but used regular expression patterns instead of globs:
{
"constraints": [
{
"from": ["^.*/ui/.*/[^X][^/]*.java$"],
"to": ["^.*/repos/.*$"]
}
]
}Disabling the architecture analysis
To disable architecture analysis, set the -Dsonar.architecture.enable property to false .
Last updated
Was this helpful?

