Using Annotations or other Criteria for Architectural Models

Sometimes the information needed to properly assign a component to an artifact is not part of its architecture filter name. Imagine for example a code generator that generates classes for different functional modules. If all those classes end up in the same package it becomes very hard to assign the generated classes to the right functional modules unless the class name contains some clue. If those generated classes could be properly assigned based on an annotation that would be a far more effective method of assignment.

The following class shows a practical example:

package com.company.generated;
 
import com.company.FunctionalModule;
 
@FunctionalModule(name = "Customer")
class E5173
{
    // ....
}

Neither the class name nor the package name contain a clue that this class is associated with the functional module “Customer”. Only the annotation gives that information away.

Since Sonargraph 9.7 it is possible to use what we call “attribute retrievers” in name patterns. In our example we would do the assignment as shown below:

artifact Customer
{
    include "JavaHasAnnotationValue: com.company.FunctionalModule: name: Customer"
}

If a search pattern contains colons it is split up into the parts separated by the colons. The first part must be the name of an existing attribute retriever, in our example “JavaHasAnnotationValue”. The last part is always a pattern describing what we would like to match and can make use of the wildcards “**”, “*” and “?”. In our example we want to match anything where the name attribute of this specific annotation is equal to “Customer”. Everything in between the first part and the last part are parameters for the retriever. Here we tell the retriever that we want to match with the “name” attribute of the annotation “com.company.FunctionalModule”. Most retrievers don’t need parameters, the example above is therefore already a pretty sophisticated use of attribute retrievers.

Available Attribute Retrievers

PhysicalFilterName

This retriever only works in the context of a logical model and will return the physical architecture filter name of a component. The component in this case would be a logical element, e.g. a class. The result is the architecture filter name of the physical component containing this element. Using this retriever allows you to mix physical and logical assignment strategies in a logical model.

WorkspaceFilterName

This retriever will return the workspace filter of any component. The workspace filter name is the relative path of the source file containing an element. This can be useful to separate assignment by root directory (e.g. test code versus generated code), since root directories are not part of any architecture filter name.

JavaHasAnnotation

This retriever only works for Java and will match if the pattern matches the fully qualified Java name of any annotation of a class. In a physical model if a Java file has more than one top level type we only consider the Java class that has the same name as the file. Please note that “*” will match anything except dots (‘.’) for this retriever.

artifact Controller
{
    include "JavaHasAnnotation: **Controller"
}

This example will match any class that has an annotation ending with “Controller”.

JavaExtendsClass

This retriever only works for Java and will match if the pattern matches the fully qualified Java name of any direct or indirect base class of a class. In a physical model if a Java file has more than one top level type we only consider the Java class that has the same name as the file. Please note that “*” will match anything except dots (‘.’) for this retriever.

JavaImplementsInterface

This retriever only works for Java and will match if the pattern matches the fully qualified Java name of any interface implemented by the class. In a physical model if a Java file has more than one top level type we only consider the Java class that has the same name as the file. Please note that “*” will match anything except dots (‘.’) for this retriever.

JavaHasAnnotationValue

This retriever only works for Java and will match if the pattern matches value of a specific annotation of a class. It has two parameters: the fully qualified Java name of the annotation class and the name of the annotation property to extract. In a physical model if a Java file has more than one top level type we only consider the Java class that has the same name as the file. Please note that “*” will match anything except dots (‘.’) for this retriever.

CSharpExtendsClass

This retriever only works for C# and will match if the pattern matches the fully qualified C# name (namespace plus class name separated by ‘.’) of any direct or indirect base class of a class. In a physical model a C# file will only be considered if it contains a type that has the same name as the file. Please note that “*” will match anything except dots (‘.’) for this retriever.

CSharpImplementsInterface

This retriever only works for C# and will match if the pattern matches the fully qualified C# name (namespace plus class name separated by ‘.’) of any interface implemented by the class. In a physical model a C# file will only be considered if it contains a type that has the same name as the file. Please note that “*” will match anything except dots (‘.’) for this retriever.

CppExtendsClass

This retriever only works for C++ and will match if the pattern matches the fully qualified C++ name (namespace plus class name with ‘.’ as separator) of any direct or indirect base class of a class. In a physical model a C++ component will only be considered if it contains a type that has the same name as the component. Please note that “*” will match anything except dots (‘.’) for this retriever.

Do you have ideas for other attribute retrievers or want to leave some feedback? Then please add a comment in the section below.

Leave a Reply

Your email address will not be published. Required fields are marked *