Software Development

Arch Unit

If Software Architecture is done to a reasonable standard, we should expect to see:

  • Well designed patterns that can fulfill both functional requirements and non-functional requirements
  • No crazy crazy coupling, concerns are properly separated and everything is testable.

If we get that, we should have confidence that as the software evolves it is maintainable. So the tricky part is all too often Architectural rules start off great on a whiteboard or a powerpoint slide, but just get lost in code, because they are too difficult to enforce.

Arch Unit is a super mechanism to enforce some Architectural rules and patterns on your code base.  It has been around a few years but something only I discovered this year.  I came across it when I was trying to think of ways to ensure the proverbial “utils” package did not turn into the proverbial “dumping ground“. Ideally, we’d have no utils packages ever, but in the real world they nearly always exist. The utils package shouldn’t have many efferent dependencies.   So for example, suppose you have a package called shoppingcart.   Then you have need some sort of utility function to add the total of two carts, remove special offers, add loyalty discounts, blah blah blah.  The last thing you want to see is someone checking that into the utils package with dependencies towards the shoppingcart package.  Because, if it is so shoppingcart focused, it should really just be in the shoppingcart package. If this happens, very soon your utils package will have dependencies to everything and everything will have dependencies to it.  Disaster. What is the point in packages if anything can just depend on anything. They will cease to provide any name-spacing or encapsulation benefits.

So, how can Arch Unit help?  Well very simple you define Architectural rules like a JUnit test.  Wait a sec…  It is a JUnit test.    The efferent (outward) and afferent (inward) for you utils package are very simple expressed as:

1
2
3
4
5
@ArchTest
public static final ArchRule utilPackageDependenciesRules = classes().that().resideInAPackage("com.company.application.util")
           .should().onlyDependOnClassesThat().resideInAnyPackage(getAllowedDependencies("com.company.application.exception"))
           .andShould().onlyHaveDependentClassesThat().resideInAnyPackage("com.company.application.shoppingcart"
 "com.company.application.payment);

So that’s it. Now repeat for every package and we have code control. Runs like any other JUnit test. So therefore will run easily as part of your CI etc. Now, if you have architected your packages well, it doesn’t have to keep coming up at code reviews. Instead the rules are part of your testing. As your software evolves and new packages come along and dependencies rules change, simple just change the rules expressed in nice fluent APIs. Someone new joins the teams and wants to get up to speed on the Architectural rules – simple, just look at the Architectural tests.

Not only does ArchUnit give you the ability to express package rules, you can also define your own rules aka conditions and then apply them to whatever code you want. For example, suppose you want a condition that an object is immutable. You naturally therefore want no setters. That could be expressed by this condition.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
static ArchCondition noPublicSettersCondition =
         new ArchCondition("class has no public setters") {
             @Override
             public void check(JavaClass item, ConditionEvents events) {
                 for (JavaMethod javaMethod: item.getMethods()) {
                     if (javaMethod.getName().startsWith("set") &&
                       javaMethod.getModifiers().contains(JavaModifier.PUBLIC)) {
                         String message = String.format(
                             "Public method %s is not allowed begin with setter", javaMethod.getName());
                         events.add(SimpleConditionEvent.violated(item, message));
                     }
                 }
             }
         };

You could then apply the noSetter condition to any custom Exception a developer may write. It wouldn’t be good if an Exception had a setter would it?

1
2
3
@ArchTest
    public static final ArchRule noExceptionsHaveSetters = classes().that()
      .areAssignableTo(RuntimeException.class).should(noSettersCondition);

Suppose you keep noticing that Loggers defined in classes either
aren’t private, aren’t static or aren’t final. Don’t waste time talking about it code reviews. ArchUnit it!

1
2
3
4
5
6
7
@ArchTest
    public final ArchRule loggers_should_be_private_static_final =
            fields().that().haveRawType(TaLogger.class)
                    .should().bePrivate()
                    .andShould().beStatic()
                    .andShould().beFinal()
                    .because("we agreed on this convention");

So the goal really here is conceptualise good rules that will help your to remain testable and maintainable, enforce them in a way that is easy to check and understand. ArchUnit really is a great library tool.

Published on Java Code Geeks with permission by Alex Staveley, partner at our JCG program. See the original article here: Arch Unit

Opinions expressed by Java Code Geeks contributors are their own.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button