Code sits below the architecture. It is not an expression of architecture. Code realizes architecture, but is constrained by it. Therefore, it is entirely possible to declare the architecture in the code.
For example, let’s say that we are building a distributed system composed of a user interface and four services. The user interface calls the services only. One of the services recognizes a business event and transmits it to an EAI system for distribution to subscribers.
The services, architecturally, should remain isolated from one another. We want to declare that… to constrain our code. We also want to make sure that the user interface is not dependent upon any interface to the services except a declared public services interface. This dependency needs to be declared and enforced. In other words, no other dependency is allowed, especially the sharing of class across the service boundary.
So how would we declare these constraints in our code? I would prefer to do this visually, because I don’t think that architecture can or should be described in some distorted text typical of most 3GL programming languages. I have no problem if the visual image has an XML representation, or some other computer-readable standardized mechanism, but the developer should access the architecture visually.
In addition, the architecture should be a starting place for the code. I would envision that when a developer opens the code for his app, he sees the diagram and uses it to navigate through the project or projects. Since this is a distributed system, compilation of each component must be fairly seperate, since each component could potentially reside in different systems.
Unfortunately, it is 1am and I don’t have the patience for writing up the diagram at the moment. I’ll slap together an example tomorrow. I envision the code representation to look something like this (note: this is a revision of this post. My first attempt was in XML but in hindsight, that wouldn’t be part of the code, it would be part of the IDE or config, so I rewrote to something that looks like C#):
architecture SCRIBE // system name is SCRIBE in this example
//by declaring DataLayer to be InProcess, that means that I intend
// to have the users of this component call it directly in a DLL rather
// than through service calls. This is important to SOA.
DataLayer.namespace = “contoso.scribe.service.data”;
layer front, service, composition;
front.namespace = “contoso.scribe.interface.front”;
service.namespace = “contoso.scribe.service”;
composition.namespace = “contoso.scribe.interface.proxy”;
layer facade, business;
facade.namespace = “contoso.scribe.service.customer.facade”;
business.namespace = “contoso.scribe.service.customer.business”;
layer facade, business;
facade.namespace = “contoso.scribe.service.invoice.facade”;
business.namespace = “contoso.scribe.service.invoice.business”;
In this example, wildly oversimplistic but illustrative, I have described two services and a user interface layer. The u/i layer declares its composition layer and the fact that it is calling services. (.channels.add) The interactions can then be limited. If a bit of code in the interface were to directly call code in ‘contoso.scribe.service.data’, then the system could warn the developer that the interface layer can only call the composition layer or the service layer. The service layer can call nothing else. The composition layer is allowed to interact using a service model.
Perhaps this ties in to Domain Specific Languages somewhat. My problem is the notion of considering this to be something seperate from the C# or VB.Net languages themselves.
We keep building things AROUND our programming languages, but I think we should not omit the potential gains by building WITHIN them as well, so that our compilers can warn a developer when they exceed the architecture.
It also means that the architect can do a code review on a single document, the declared architecture, and feel reasonably assured that the document that he or she is looking at actually reflects the intent of the system, because the compiler is working for the architect, to enforce and reassert the architectural intentions if the developers make a mistake.