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)However, that's not all. I know of some groups of dependencies that do not follow these high-level patterns.
(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)
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:LogAlso, 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.** ---? Archichect:DictionaryExtensions
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.
Archichect:Program ---> Archichect.Rendering.TextWriting:DipWriterIs 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:
Archichect:Program ---> Archichect.Transforming.ViolationChecking:CheckDeps
Archichect:Program ---> Archichect.Rendering.TextWriting:RuleViolationWriter
Archichect:Program ---> Archichect.WebServing:WebServer
- A few utility classes (e.g. DirectoryExtensions or the Option classes)
- The fundamental Item and Dependency class structure.
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:Mhm—I should have considered that the PathMatching helper needs AbstractDependency, similar to the Matching helper. I could just allow this:
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)
Archichect.Matching ---? Archichect:AbstractDependencyWith 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'...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.
-- ;;1'_declaresparameter
-> DOTNETPARAMETER:Archichect:GlobalContext:Archichect;0.0.0.5;:globalContext'_in
(at archichect.exe/Archichect.Matching.DependencyMatchOptions.Parse)
No comments:
Post a Comment