Zügel for C#

With our newest 26.5.1 release of the Zügel MCP server we added C# as a third supported language. This article only explains the C# setup and C# specific features. If you want to learn what Zügel is and what it can do for you, please read our introduction article.

C# Setup

As always you create an .mcp.json file that tells your agent how to start Zügel, start the agent in the root directory of your solution, and ask it to call generate_config followed by reload_all. After that the agent will analyze your project and create the initial baselines.

Two things are different for C#. You need the .NET 10 SDK — the SDK, not just the runtime, because Zügel opens your solution through MSBuild and only an SDK ships MSBuild. And Zügel needs a .sln: if your repository has several, one is picked, written into the configuration and the others are named, so pointing it somewhere else is an edit rather than a guess. Nothing is downloaded — the Roslyn based parser we also use in Sonargraph ships inside the Zügel jar — and if the solution’s NuGet packages have never been restored, the first scan restores them for you.

This is how the generated zugel.json configuration file looks like for typical C# project:

{
"language": "csharp",
"project": {
"solution": "src/MyApp.sln",
"modules": [
{ "name": "MyApp", "project": "MyApp(net10.0)",
"sourceRoots": ["src/MyApp"],
"generatedSourceRoots": ["src/MyApp/obj/Debug/net10.0"] }
],
"generatedPatterns": ["**/*.designer.cs", "**/*.generated.cs", "**/*.g.cs"]
},
"arcFiles": ["architecture/MyApp.arc"]
}
Read More

Zügel Just Learned Python

With our newest 26.4.1 release of the Zügel MCP server we added Python as a second supported language after. This article only explains the Python setup and Python specific features. If you want to learn what Zügel is and what it can do for you, please read our introduction article.

Python Setup

As described in the introduction you need to create an .mcp.json file that tells your agent how to start Zügel. Then start your agent in the root directory of your Python project and ask it to use the generate_config tool to generate the zugel.json file followed by a call to reload_all. After that the agent load the will analyze your project and create the initial baselines. It will inform you about existing cyclic dependencies on the file and directory/package level. If you add architecture definition files it will also check your architectural rules.

The Python version of zugel.json looks like that:

{
  "language": "python",
  "project": {
    "modules": [
      { "name": "widget", "sourceRoots": ["src"] }
    ],
    "generatedPatterns": ["**/*_pb2.py", "**/*_pb2_grpc.py", "**/generated.py"]
  },
  "arcFiles": ["architecture/Widget.arc"]
}

The configuration for tolerated cyclic dependencies is the same as in Java. To mark generated code you can provide a list of patterns that match generated Python files. This is relevant for cycle analysis. Cyclic dependencies only consisting of generated files are tolerated automatically.

Read More

Major Changes to our C/C++ System Model

When we added C/C++ to Sonargraph our work was heavily influenced by John Lakos famous book “Large Scale C++ Design”. In this book Mr. Lakos presented a solid system to define the architecture of C++ systems based on components. A component in the most simple case is the combination of a header file and a source file, e.g. User.h and User.cpp. The header declares all elements that can be used from outside of the component, while the source file contains the implementation of the functionality. This unites the header and the source file into a logical component, that is better suited for dependency analysis.

Read More

How to Break a Big Ball of Mud?

Many non-trivial systems end up as a big ball of mud, not because developers are lazy or reckless, but because it is very hard to avoid that outcome without proper tooling. For example, if your architecture rules are spread by word of mouth or some articles in your company wiki, there is no way of knowing if the code actually conforms to any of your architecture rules. If rules are broken, most of the times developers are not aware of that. That will lead to the erosion of architectural boundaries (if they ever existed) and more and more cyclic dependency groups. In the beginning the cyclic groups start small, but they grow like cancer in your codebase. I actually did some research on that by tracking some open source projects over time. That research confirmed my assumption – if you do not address the problem of ever growing cyclic dependency groups things will only get worse over time, in some cases much worse.

Read More

Spring Modulith & Sonargraph – Better Together

We created Sonargraph with the vision in mind, that it would allow architects to formally specify an enforceable architectural model. Another goal was to provide exceptional dependency visualization capabilities, so that issues could be easily detected not only in a formal way, but also by just looking at a dependency graph. Sonargraph’s architecture DSL (domain specific language) solved the first problem, while our exploration view solved the second one in a very unique and scalable way. The DSL is quite powerful and easy to learn. For an introduction you could read “How to Organize your Code” on this very site.

But obviously we were not the only ones thinking about a way to formally define architectural rules. Spring Modulith turned out to be a very powerful and successful solution to define domain driven architectures for Spring-Boot applications. Spring Modulith follows a pretty simple hands-off approach that allows the checking of architectural boundaries with a minimum configuration approach.

Read More

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.

Read More

Logical Architecture Models

With Sonargraph 9.7 our architecture DSL gets a new feature: logical models. Up to 9.7 the concept of a component (the smallest unit assignable to an architectural artifact) was based on the physical layout of your project. So components in a physical model are based on source files and their relative location in the file system. In a logical model components are the top level programming elements in a namespace or package and their name contains the namespace and no relative path.

Read More

Adding Transitive Dependencies to the Architecture DSL

This post assumes that you are already familiar with Sonargraph’s architecture DSL. If not, I recommend first reading “how to organize your code”.

Transitive dependencies are a useful addition to formal architecture descriptions. The following example shows a typical use case:

artifact Controller
{
    include "**/controller/**"
    connect to Foundation
}
 
artifact Foundation
{
    include "**/foundation/**"
}

Here Controller depends on Foundation. We also assume that classes from Foundation are used in the public interface of the controller classes. That means that each client of Controller must also be able to access Foundation.

artifact ControllerClient
{
    include "**/client/**"
    connect to Controller, Foundation
}

This is certainly not ideal because it requires the knowledge that everything that uses the Controller artifact must also connect to Foundation. It would be better if that could be automized, i.e. if anything connects to Controller it will automatically be connected to Foundation too.

With Sonargraph 9.6 this is now easy to implement:

artifact ControllerClient
{
    include "**/client/**"
    connect to Controller // No need to connect to Foundation explicitly
}
 
artifact Controller
{
    include "**/controller/**"
    connect to Foundation transitively
}
// ...

Using the new keyword transitively in the connect statement will add Foundation to the default interface of Controller. That means that anybody connecting to the default interface of Controller will also have access to Foundation without needing an explicit dependency.

The new keyword only influences the default interface. For explicitly defined interfaces the transitive export also has to be made explicit:

artifact ControllerClient
{
    include "**/client/**"
    connect to Controller.Service // Will also have access to Foundation
}
 
artifact Controller
{
    include "**/controller/**"
 
    interface Service
    {
       include "**/service/**"
       export Foundation // Transitive connection must be explicit
    }
 
    connect to Foundation transitively // only affects default interface
}
// ...

Before we had transitive connections an interface could only export nested artifacts. Now interfaces can also export connected interfaces. In the example above we add the default interface of Foundation to the Service interface of Controller. Exporting interfaces that are not a connection of he parent artifact will cause an error message.

This feature is available with Sonargraph 9.6 or higher. Let me know what you think about it in the comment section below.

Designing Generic Architectures Using Templates

Many companies already have some established architectural design patterns which are supposed to be used in most of their applications. For example it makes sense to standardize the layering of business components. It also makes sense to establish specific rules how one business component can access another one. In the upcoming 9.4 release of Sonargraph-Architect we implemented a new feature in our architecture DSL which should make it very easy to add generic architectural blueprints to a quality model which would allow automatic verification of those architectural design patterns on any business component without having to create a component specific architecture.

Read More

Evolving Sonargraph’s Architecture DSL

Sonargraph’s architecture DSL is now about 18 months old and we received a lot of positive feedback from customers bundled with ideas for improving the language. There are now several projects with more than one million LOC that use this language to define and enforce their architectural blueprint. Of course this feedback is most valuable for us and we did our best to implement a good share of the ideas brought to us. This article requires some basic knowledge of our architecture DSL. An introduction can be found here. To use all the features described below you need Sonargraph-Architect version 9.3 or higher.

Expressing Architectural Patterns as Artifact Stereotypes

There are some basic patterns that are used in almost every architectural model. Those patterns describe the relationships between sibling artifacts, i.e. artifacts that have the same parent.

  • Layered architecture – here dependencies are allowed to flow top-down within an ordered list of sibling artifacts. If we use strict layering, an artifact can only access ist next sibling artifact. In the case of relaxed layering, artifacts have access to all artifacts defined beneath them.
  • Independent – here sibling artifacts are independent from each other, i.e. there should be no dependencies between them.
  • Unrestricted – here siblings artifacts have no restrictions in accessing each other. This is not very desirable because it will allow cyclic dependencies between artifacts, but can be really useful when working on a model for a legacy software system.

Read More