Archive for the 'Architecture' Category

Mapping Architecture Diagrams to Code – the “most specific pattern” rule


Architecture Diagrams in Structure101 are mapped to the physical code by patterns associated with each cell in the diagram. This enables the visual specification of rules that can then be applied to a specific version of your code so that Structure101 can overlay any violations on the diagram and let you discover the offending code items.

When you let Structure101 create the diagrams for you, and stick to changes like adjusting the layering, expanding packages, or moving packages to new parents, etc., you can mostly leave it to Structure101 to handle the patterns. However, occasionally you may see some behavior that seems odd unless you understand how patterns interact.  And of course you may want to take advantage of the more advanced capabilities.

A key aspect that is not obvious at first is the “most specific pattern” rule. This says that where a physical class maps to more than one cell, the Architecture Diagram associates the class with the cell that has the “most specific” pattern.

Here’s an example diagram which was automatically generated from part of the Findbugs code-base:

18-11-2009 15-48-38

… and here is the same diagram showing the pattern associated with each cell:

18-11-2009 16-16-02

If I have a code-base that happens to contain a class edu.umd.cs.findbugs.classfile.impl.ClassFactory, then that class matches the pattern associated with the cell called ClassFactory. However it also matches the patterns for both ancestor cells “impl” and “classfile”. Since the ClassFactory cell’s pattern is the most specific (no wildcards), that is the cell to which Structure101 associates our physical class. And since a class is associated with at most one cell, that class is not associated with either ancestor cell, even though it matches their (less specific) patterns.

As expected, here are the associated items that Structure101 reports for the ClassFactory cell:

18-11-2009 16-51-41

… and the parent “impl” cell:

18-11-2009 16-55-15

Now I delete the 3 inner cells so that the diagram looks like this (the patterns for the remaining cells are unaltered):

18-11-2009 17-11-59

Since the cells with the more specific patterns are gone, the classes that were associated with them now match the next most specific pattern – the parent “impl” cell:

18-11-2009 17-17-54

This can be puzzling – I click on the “expand” button, the child cells are added to the diagram (which I expect), but the list of items associated with the parent is suddenly empty (which I don’t).

Why would we do it this way? We figured it was the best way to be future proof (the wildcards handle any added/removed classes) while at the same time supporting refactoring (e.g. if I move a class to a new package, the most specific pattern goes with it, but all the other classes still match to the original parent).

 

Structure101 3.1 – Software Architecture Sandboxing


Just released, Version 3.1 adds lots of new stuff to the Architecture perspective to make it much easier to discover the current structure and move classes or packages around to define a preferred architecture.

First thing is a simple expand and collapse button on each cell. So for example you can ask Structure101 to create a high-level architecture diagram from the current code-base – no need to worry about how deep to make the initial diagram since you can now expand and collapse cells once you have the initial diagram.

Let’s say you get the following diagram:

Architecture Diagram 1

This shows a layering violation from the strut2 package to the dispatcher package. Click the “+” to find out what is being used at the next level of detail.

Architecture Diagram 2

So something in struts2 is using something in dispatcher.controller. Expand both cells…

Architecture Diagram 3

And we see there is a single class-to-class dependency causing the violation. Also, since the Dispatcher class is below the other classes in the dispatcher.controller layering, we know that the other classes use, but it does not use the other classes. And the layering in the struts2 package indicates that we can move Dispatcher to the indicated level to fix the violation without creating new ones. Just drag and drop Dispatcher to create this:

Architecture Diagram 4

Collapsing the 2 top-level cells by clicking the “-” icons shows the original layering but with the underlying code rearranged so there is no longer an architecture violation.

Diagram5

Other stuff that has been added in this version:

  • Once you have moved classes or packages, you can get a list of all the moved items.
  • You can convert the list of moved items into transformations on the underlying model. This lets you iterate on a restructuring job – use an architecture diagram as a kind of “Sandbox” until you have some set of changes you’re happy with, then convert them into transformations and start sandboxing the next set of changes.
  • You can now apply strict layering to a diagram. This means that cells can only use cells immediately below them, not below that.
  • You can show any dependencies (i.e. not just violations) either on the whole diagram or on selected cells.
  • You can drag items onto an architecture diagram from the dependency breakout.

Final note, if you are using the architecture diagrams to drive your developers, you should choose the expansion level of cells consciously before publishing them to the project repository. Their code changes will be checked against the visible levels only.

 

Spring 2.5 Architecture Diagrams


I have updated the architecture diagrams for the just-released Spring 2.5. Any new or changed packages are highlighted (since 2.0.6). The diagrams are also online – if you pointed your IDE plugin at these after my previous entry, you will be seeing the updated diagrams in your IDE already, and any compile time messages about architecture violations will be based on the new versions.

Here’s the new top-level architecture:

Top

And here are the internals of the larger subsystems:

org.springframework.aop:

Aop

org.springframework.beans:

Beans

org.springframework.jdbc:

Jdbc

org.springframework.jms:

Jms

org.springframework.orm:

Orm

org.springframework.web:

Web

 

Structure101 V3 Released, Adds Architecture Control for Teams


Released today, the new version 3 capabilities make Structure101 a nicely rounded architectural control solution in addition to the previous structural analysis and complexity measurement capabilities.

For example:

  • You can now define layering constraints on your code-base using simple, intuitive architecture block diagrams
  • Communicate these architecture diagrams to the development team through IDE plug-ins
  • Developers get warned immediately if they make code changes that are inconsistent with the architecture
  • RSS activity feeds let you know if new architecture violations make it into a build

Also, there’s a new online demo (about 13 minutes, with audio (me!)) and the version 3 Help is available online.

Full press release

Enjoy!

 

Package design matters – Part 1


Java packages are often used like file-system folders to organize source. But source files differ from “normal” files in that they are highly inter-dependent. Considering this interdependence as a package hierarchy evolves can have significant productivity benefits.

Packages as Folders

Java packages provide an ideal way to organize code into a scalable, hierarchical structure that helps us find specific code.

In this sense, packages can be used like folders in a file-system:

  • We place files with something in common in the same folder.
  • When a folder grows too big and we find we’re having trouble finding files, we split the folder into sub-folders according to some criteria that makes sense to us.
  • We share files by placing them in a common area on company network, in which case the structure evolves according to the varying criteria of different people.
  • We often have trouble deciding which folder a file best belongs, and make an arbitrary decision.

Often Packages are only used as a kind of filesystem equivallent.  However the package hierarchy can also be used to reinforce the intended design and associated development activities.

Packages as Design Abstractions

Source files differ from other files typically stored in the file-system:

  • They depend on the detailed contents of other source files.
  • Are created and edited in groups of multiple files.
  • Are subject to a high number of relatively small changes.
  • Are edited by a team of developers rather than individuals.
  • Should be reusable on future projects.
  • Should be easy to change without impacting other files too widely.
  • Must support the defined deployment environment.
  • Are subject to a QA, version control and release processes.

The aim of a package design should be to support these characteristics.  For example, the design could explicitly support Martin’s “Reuse/Release Equivalence Principle (REP)” (article, book) whereby packages are developed, built, tested and released against released versions of the packages upon which they depend.

Design is not something that happens once at the start of the project – it is an activity that spans the life of an application or product.  This fact has become explicit with the iterative and Agile development models.  As the code-level design continues, the package-level design emerges.
Unfortunately, the emergent design is often invisible and so forgotten.  Not only does the original design degrade, but the overall structure tends to become excessively complex.  As the supporting structure dissolves, development activities become harder and the cost of each new feature increases.

This priciple of emergent design is important here. Clearly when I have a project of 50 classes in a half-dozen packages, the overhead of a sub-optimal package dependencies isn’t going to slaughter me. But if my project is going to grow to 5000 classes, then putting in minimal effort from the start can save huge effort when things get more complicated.

In part 2 I’ll take a closer look at cyclic package dependencies and why they matter.

 

An Overview of Structure101 Architecture Diagrams


Structure101 lets you work with both structure (the whole code-base as it is) and architecture (the subset of the structure that you really care about, and how it should be). It lets you define the architecture in the context of the physical structure and diseminate this to the team. Architecture diagrams are what makes this possible.

Layering and composition

Structure101 architecture diagrams use a concise visual notation for representing architectural layering and composition. Here is an example of one of the architecture diagrams that we use for the structure101 code-base.

Layeringandcomposition_3
The principle is simple; components (“cells”) should only depend on components at lower levels, not in the same or higher levels.

Layering Overrides

Sometimes a top-down dependency structure is too simple to capture the intent of an architecture. “Overrides” allow you to override the default layering of a diagram. For example we may decide to allow a specific dependency from a cell to a higher-level cell. The override is shown as a green (“allowed”) arrow on the architecture diagram. (Note that enabling this “upward” dependencies practically  merges the “hiView”, “xbase” and “graph” components from the perspective of testing, reuse, development, etc.)

Layeringoverrides
A more common example is where we wish to enforce a more strict layering. For example we may want one layer to only use the next layer down, but not layers below that. Such an override is shown as red (“disallowed”) on the architecture diagram.

Combining diagrams

It is not necessary to include all aspects of an architecture on a single structure101 architecture diagram.

A common scenario is where a number of “add-ins” are distributed across several packages. For example, this diagram shows part of the structure101 architecture.

Combiningdiagrams1
It is correct, but incomplete. Classes in assemblies.X should never depend on classes in lang.Y.  We could express this by adding several overrides, but it is much cleaner to use a separate diagram for this aspect of the architecture.

The next diagram defines a number of “language packs” that do not have a direct equivalent in the physical structure (they are “pure” architecture components), but express the architectural constraint that was missing above.

Combiningdiagrams2
The combination of the 2 diagrams defines the intended architecture.

Mapping the architecture to physical code

In order to understand how a physical code-base conforms to an intended architecture, we need to map the architectural components (cells) to physical code.

Simple patterns are used to establish this mapping. This has a number of benefits:

  • If a diagram contains a component mapped to com.headway.lang.* and the team creates a new package com.headway.lang.cobol, then the diagram is not rendered obsolete – all the classes in the new package map to the intended component.
  • I can create components with more complex mappings with expressions such as com.headway.*.test.?
  • I can create and show a component for which no code has yet been created, either by specifying no pattern or specifying the paths where I expect the new code to be implemented.
  • I can effectively “hide” physical entities from a diagram. For example any code in com.headway.lang.cobol will simply map to a component with the expression com.headway.lang.* – I do  not need to show package cobol on the diagram if I don’t want to.

Another flexibility is that a physical entity maps to the component with the most specific pattern.  For example if I include 2 components, one with com.headway.lang.* and the other with the expression com.headway.lang.java.*, then the class com.headway.lang.java.myClass will map to the latter. The effects of this can be at the same time subtle and powerful. For example I could move the component that maps to com.headway.lang.java.* into another “parent” altogether.

Finally, each diagram has a (possibly empty) expression that maps to  “excluded” items. This is useful if some physical entities would otherwise undesirably map to a component in the diagram.

Once the mapping is established, any dependencies that violate the architecture is shown on the diagram as a curved dotted line as shown here between component “graph” and the higher-level package “hiView”.

Maptophysical
It is easy to discover the code-level cause of a violation by selecting it on the diagram within a structure101 client or IDE plug-in.

 

Structure101 IntelliJ Plug-in Build 104


This update now checks for architecture violations automatically when you do a build (previous version was “on-demand” only).  More on structure101 IDE plug-ins.

 

Code Organization Guidelines for Large Code Bases


In an excellent on-line presentation Juergen Hoeller gives rationale and guidelines for controlling the structure of large, evolving code-bases. Juergen is the chief architect of the Spring framework, which as I have previously pointed out is structurally almost perfect. This didn’t happen by accident.

If you don’t have time go though the 88 minute presentation, here is a nice sysnopsis by Mike Nereson.

 

Structure101 IntelliJ Plug-in


This has just been made available for download. It displays architecture diagrams within the IDE, and warns if any code changes are inconsistent with the target architecture.

Intellijsmall

It works more or less like the structure101 Eclipse plug-in except that for now it checks for architecture violations on demand rather than automatically when you do a build.

 

Structure101 Supports Java 1.5


We have overhauled the byte-code parser to now pick up Java 1.5 constructs (e.g. generics and annotations), plus it is much faster.

You can download the first build that includes the new parser here.

If you currently use the structure101 IDE plug-in you may find that you get bogus warnings on "new" architectural violation when you start using the new parser. This is  because the parser is now finding a few more dependencies related to the Java 1.5 language updates, and thinks they are new compared to your reference snapshot. If it’s giving you a headache, you should just republish your reference snapshot to the structure101 repository with the new parser.