However, after we did that for the Archichect tool itself, there were still more than six thousand dependencies that violate the first, simplistic set of rules that I derived from a rough overview. What are the reasons for these violations? Essentially, there are two:
- First, Archichect uses some libraries to provide its features—e.g. Mono.Cecil for reading assemblies, or System.IO for reading and writing files.
- Second, the first attempt of describing Archichect's internal architecture was too simple-minded—thus, there are many dependencies between parts of Archichect that are not captured by my first set of rules.
Before I start with the concrete rules, I would like to know how many of the present violations are of the first kind—i.e., referring external classes—, and how many of the second. For this, I simply add a rule
Archichect.** ---? Archichect.**This rule allows every item inside Archichect to use any other Archichect item. Of course, I must take care to remove this rule from the final rule set afterwards. That's the reason why I chose the "question mark arrow", which will mark all matching dependencies as "questionable"—hopefully, I will remember to remove this rule later (an additional comment might also be helpful ...).
With this rule added, Archichect's output is Writing 1438 violations and 4717 questionable dependencies. Thus, we will deal in this step with around 1400 dependencies—the rest will be tackled in the next posting.
Now, what about these "violations"? Among other things, Archichect can do the following (I will explain the reasons for these features later, maybe much later—right now, assume that these features are just a "given"):
- It can extract dependencies from .Net assemblies (".DLLs" and ".EXEs");
- it can watch files (e.g. rule files or source files) and automatically rerun a check in a new thread when a file changes;
- it can create some architectural diagrams;
- of course, it accesses files and directories at many places;
- it provides a small web server for accessing output via a browser;
- it can write rule violations in an XML format (but right now not JSON, which is an unforgivable oversight; I will repair this soon);
- it can load plugins from assemblies for reading, transforming, writing, and more;
- it caches items and strings internally for more efficient memory usage.
- Reading .Net assemblies: Classes in namespace Archichect.Reading.AssemblyReading;
- file watching: Class FileWatcher in namespace Archichect;
- diagram creation: Classes in namespace Archichect.Rendering.GraphicsRendering;
- file and directory access: many classes need this for reading (of dependencies or configurations) and writing (of violations, logs etc.); so I allow it just for all of Archichect.
- webserver: Classes in namespace Archichect.WebServing;
- XML writing: Class RuleViolationWriter in namespace Archichect.Rendering.TextWriting;
- loading plugins: Classes GlobalContext and ItemAndDependencyFactoryList in namespace Archichect;
- Caching of items: Classes in namespace Archichect and child namespaces Archichect.Reading and Archichect.Rendering; in turn, the classes implementing the cache use System.Threading.
Archichect.Reading.AssemblyReading ---> Mono.(Cecil.**|Collections.Generic)And now—ta-ta-ta-taa: Running Archichect with these rules leaves us with a mere 40 violations, down from the 1438 just minutes ago! It seems I have described an interesting part of Archichect's architecture almost perfectly (but, well, I invented the tool and wrote its code, so it would be fatal if I couldn't accomplish this, wouldn't it?)
// For reading .Net assemblies, Archichect uses Mono.Cecil,
// including some general Mono collection classes
Archichect:FileWatcher ---> System.ComponentModel:Component
// .Net's FileSystemWatcher derives from Component
Archichect:FileWatcher ---> System.Threading
Archichect.Rendering.GraphicsRendering ---> System.Drawing.**
Archichect.** ---> System.IO
Archichect.WebServing ---> System.Net
Archichect.Rendering.TextWriting:RuleViolationWriter.** ---> System.Xml.**
Archichect(.Reading.**|.Rendering.**)? ---> Gibraltar
// String caching I copied from the web and generalized for arbitrary objects
Gibraltar ---> System.Threading
What about these last 40? A quick scan of them exposes the following two reasons:
- A somewhat widespread use of System.Reflection.MemberInfo, where its Name property is accessed. This results from code like ...GetType().Name, which I find totally ok. On the other hand, I do not want to allow use of all of System.Reflection everywhere, therefore I add the specific rule
- The GlobalContext class uses System.Threading's CancellationTokenSource and CancellationToken as well as System.Reflection's Assembly and AssemblyName classes. I could now add another two rules going from GlobalContext to ... but wait: Am I still doing architecture here, or is this going into nitty-gritty details that concern no one?
Archichect.** ---> System.Reflection:MemberInfo::get_Name
- "narrow", i.e., allow only access from and to items where we positively need this for current features?
- Or should it be "broad", i.e., allow the use of large swaths of foreign libraries in most of the product so that future extensions and modifications can use them freely?
Still, threads and reflection are not something to use lightly. So, I will restrict the use of these two packages to the cancellation and assembly related classes, respectively; but allow that they be used everywhere in Archichect.
Of course, in your environment, there might be hard and fast rules that must be followed no matter what—so much better so that one can define and check precise dependency rules with tools like Archichect (when and if it is completed).
However, these are still lacking for the more than 4000 open "violations" flagged inside Archichect. The next posting is a stepping stone to tackling them.
No comments:
Post a Comment