11.1.  Models, Components and Artifacts

To describe architecture in a formal way we first need to think about the basic building blocks that we could use to describe the architecture of a system. The smallest unit of design is what we call a component. What is represented by a component depends on the base model you choose for you architecture.

Since version 9.7 Sonargraph supports two different base models. The "physical" model - which is the default model and the only model that was supported prior to 9.7 - is based on the model in the "Navigation View". Components are based on the physical layout of your project. In Java a component is a single source files. In C# a component is a single C# source file or a top level type in an external assembly. In C/C++ components are created dynamically out of combining associated header and source files.

The "logical" model is based on the model in the module based namespace view. Here our components are top level programming elements, which for Java or C# is always some type, usually a class or an interface. The logical model organizes these types only by their namespaces/packages. The directory structure of the project is not reflected in the model. In C/C++ components can also be functions or other top level programming elements. For Java there is almost no difference between the physical and the logical model. Only in the rare case that a Java file has more than one top level type the logical model would create one component for each top level type, while the physical model only generates one component per source file.

So logical models are more interesting for languages like C++ and C# where the namespace structure is not related to the physical organization of your project. For these languages it makes sense to use the logical model if your namespaces are in some way reflecting your architectural design.

To define an architecture you would group associated components into architectural artifacts. Then you could group several of those artifacts together into higher level artifacts and so on. For each artifact you would also define which other artifacts can be used by them.

Each component has a name which we call the architecture filter name. In the physical model the filter name starts with the module name or "External [language]". Then follows the path of the component relative to a module specific root directory. The filter name ends with the name of the source file without an extension, All name parts are separated by slashes.

TIP

To determine the architecture filter name of a component just click on the component in the navigation or namespace view and check the "Properties View". There you should be able to see the architecture filter name and other properties of the selected item.

When using a logical model the filter name again starts with the module name followed by the namespace followed by the name of the programming element. Each name part is again separated by slashes.

In most cases assignment of components to artifacts is based on their architecture filter name. But it is also possible to assign components based on other attributes like annotations or implemented interfaces. This will be explained in more detail later in this chapter.

// Main.java in package com.hello2morrow:
"Core/com/hello2morrow/Main"
 
// The Method class from java.lang.reflection:
"External [Java]/[Unknown]/java/lang/reflect/Method" 

// SimpleAction.cs in subfolder of NHibernate:
"NHibernate/Action/SimpleAction" 

// An external class from System.dll:
"External [C#]/System/System/Uri"

For internal components (components that actually belong to your project) we use the following naming strategy:

module/rel-path-to-project-root-dir/source-name (physical)

module/namespace-or-package/element-name (logcal)

For external components (third party components used by your project) we use a slightly different strategy. Here we might not have access to any source files:

External [language]/jar-or-dll-if-present/rel-path-or-namespace/typename (physical)

External [language]/jar-or-dll-or-header/namespace-or-package/element-name (logical)

Now we can use patterns to describe groups of components:

// All components from the Core module with "business" in their name:
"Core/**/business/**"

// All components in java.lang.reflect:
"External*/*/java/lang/reflect/*"

As you can see a single '*' matches everything except a slash, '**' matches over slash boundaries. You can also use '?' as a wildcard for a single character.

Now we can build our first artifacts:

model "physical" // or "logical"
    
artifact Business
{
    include "Core/**/business/**"
}

artifact Reflection
{
    include "External*/*/java/lang/reflect/*"
}
    

In the first line you specify which model you would like to use. If you omit the model specification we assume "physical".

We grouped all components from module "Core" with "business" in their name into an artifact named "Business". The reflection classes from the Java runtime are now in their own artifact called "Reflection". Artifacts can also have "exclude" filters. They help you to describe the content of an artifact with an "everything except" strategy. Exclude filters will always be applied after all include filters.

TIP

More than one "include" statement can be used to assign components to an artifact.nIt is also possible to use "exclude" statements to specify exceptions from the elements included above.