Where you might want to start reading ...

Is there something wrong with software architecture - or with us?

I am a software architect (one of a few) for a 20-million LOC business software, with currently a few thousand installations, developed and ...

Tuesday, June 20, 2017

Archichect's package architecture checked with Archichect

"Eat your own dog food", they say—last call for Archichect's package (or rather, namespace) rules to be checked by Archichect itself.

Here is the intended structure—it is essentially what I have shown in that first posting about Archichect, but enhanced with
  • two more sets of feature packages: "Calculating", which allows for helper plugins to "compute something"; and "WebServing", for a small webserver intended for browser access.
  • five helper packages: A general constraint solver for linear inequalities, the caching facility Gibraltar, support for markers ("Markers"), dependency and item matching ("Matching"), and matching of complete paths of dependency sequences ("PathMatching")
  • and a sneaked-in class diagram for the main data structure of Archichect:


In Archichect's rule syntax, the package dependencies one can derive from this diagram might be written as follows:
// Nested feature packages can access their master package(e.g.CheckDeps->Transforming)
(Archichect.(Calculating|Reading|Rendering|Transforming)).** ---> \1

// Allow nested feature packages access to common classes (e.g. CheckDeps->Dependency)
(Archichect.(Calculating|Reading|Rendering|Transforming)).** ---> Archichect
Archichect.WebServing ---> Archichect

// Allow access to helper packages from everywhere
Archichect.** ---> Gibraltar
Archichect.** ---> Archichect.(ConstraintSolving|Markers|Matching|PathMatching)
However, that's not all. I know of some groups of dependencies that do not follow these high-level patterns.

The first group of them stems from some of these "utility bottoms" classes mentioned already in the previous posting. Let me state the relevant rules here—but I mark all of them as "questionable" (with the ---? declaration), so that I can distinguish them from "ok" rules.
Archichect.** ---? Archichect:Log
Archichect.** ---? Archichect:DictionaryExtensions
Also, I allow the Matching helper package to back-reference Archichect's ItemType, Item and Dependency classes, including their base classes. That's an example of the "utility bottom" explained in the last posting:
Archichect.Matching ---? Archichect:(ItemType|Item|AbstractItem|ItemSegment|Dependency|AbstractDependency)
The same helper package also contains a support class for options for setting up matchers, which is needed by quite a few transformers. For parsing the options, this class may access the utility Option class in the main package. Come to think of it, I would allow this from everywhere if some other helper package also would like to provide such a reusable option support:
Archichect.* ---? Archichect:Option*
A second set of "other" dependencies goes from Archichect's top level class Program to the constructors of some feature classes:
  • Archichect's -write-dip option writes a .dip file (file with textual representation of dependencies), so Archichect's top level class Program directly references the DipWriter class.
  • When Archichect is only passed a CheckDeps configuration (in addition to input files), it will do dependency checking with the CheckDeps transformer and then write the resulting violations with RuleViolationWriter. Also these two classes are, therefore, directly referenced by Program (this is, essentially, a useless behavior—saving a few trivial command line options is not the path to enhanced usability. I should remove this).
  • Finally, Archichect's -http-run option creates a WebServer object.
I consider these to be features, so the following "down dependencies" are ok, even though they create cycles in the current package layout:
Archichect:Program ---> Archichect.Rendering.TextWriting:DipWriter
Archichect:Program ---> Archichect.Transforming.ViolationChecking:CheckDeps
Archichect:Program ---> Archichect.Rendering.TextWriting:RuleViolationWriter
Archichect:Program ---> Archichect.WebServing:WebServer
Is this small static architecture ok? Well, yes and no: It is certainly (I hope you agree) a tidy architecture which knows what goes where. However, while writing the rules, it has become clear to me that there are two types of classes that are referenced from many places, yet they "hide" in the top Archichect package—there should be separate packages for these:
  • A few utility classes (e.g. DirectoryExtensions or the Option classes)
  • The fundamental Item and Dependency class structure.
A third problem is that the Program class forms a sort of "top level spider" which needs access to more classes than others—also this should be honored with a separate package (which would contain only one class!). Some work to do—and it might have unintended consequences: A perfect example for exploring an experimental architecture—I'll try that in the next posting.

But now, let me check how far Archichect is away from its intended architecture. I run it on itself ...

... and get 2 violations. Well, that should have been zero, but I got quite close. What did I overlook? Number one is
Bad dependency detected by global rule group:
DOTNETMETHOD:Archichect.PathMatching:AbstractRegexDepthFirstPathTraverser:Archichect;0.0.0.5;:Traverse
  -- ;;1'_usesmember
  -> DOTNETMETHOD:Archichect:AbstractDependency:Archichect;0.0.0.5;:get_UsedItem
  (at C:\PT\github\Archichect\src\Archichect\PathMatching\AbstractRegexDepthFirstPathTraverser.cs/127)
Mhm—I should have considered that the PathMatching helper needs AbstractDependency, similar to the Matching helper. I could just allow this:
Archichect.Matching ---? Archichect:AbstractDependency
With moving AbstractDependency to a separate package, I would also remove the question mark, and I would be happy. Or would I? That there is only one (1!) dependency from PathMatching—a package with about 900 lines of code—to AbstractDependency hints at an opportunity: Namely, to completely get rid of it! To-do item number three (after extracting one or two utility packages and a top-level package): Decouple PathMatching from AbstractDependency.

Last, there is
DOTNETMETHOD:Archichect.Matching:DependencyMatchOptions:Archichect;0.0.0.5;:Parse'...
  -- ;;1'_declaresparameter
  -> DOTNETPARAMETER:Archichect:GlobalContext:Archichect;0.0.0.5;:globalContext'_in
  (at archichect.exe/Archichect.Matching.DependencyMatchOptions.Parse)
The GlobalContext is—well, the global context. Yes, it is passed around (too) heavily, but it should not go into helper packages. However, option parsing, right now, always needs the global context, so it would be hard to get rid of this. Maybe I should move DependencyMatchOptions to the main package, after all? Or to the proposed options helper package? This looks very much like another opportunity for exploring an "experimental architecture"! Hopefully, I can show how to do a little bit in the next posting.

No comments:

Post a Comment