11.12.  Designing Generic Architectures Using Templates

Many companies already have some established design patterns which are supposed to be used in most of their applications. For example it makes sense to standardize the layering of business components. It also makes sense to establish specific rules how one business component can access another one. The template feature in our architecture DSL makes it very easy to add generic architecture blueprints to a quality model which would allow automatic verification of those architecture design patterns on any business component without having to create a component specific architecture.

For generic architectures to work properly it is a good idea to think about code organization, in particular the efficient use of name spaces or packages to reflect architectural intent. That can be easily done by using naming conventions:

com.hello2morrow.{component-name}.{layer-name}
    

In this simple example we assume that the component name is always the third part of a package/namespace name. The fourth part represents the layer. Knowing that we can now create a generic architecture description for this example:

// aspect file layering.arc
strict exposed artifact Service
{
    include "**/service/**"
}

strict exposed artifact Controller
{
    include "**/controller/**"
}

require "JDBC"

exposed artifact DataAccess
{
    include "**/data/**"
    connect to JDBC
}

public exposed artifact Model
{
    include "**/model/**"
}

public exposed optional artifact Util
{
    include "**/util/**"
}

deprecated hidden artifact Leftovers
{
    include "**"
}

// main file components.arc
template Components
{
    include "**/com/hello2morrow/(*)/**"
    exclude "**/com/hello2morrow/framework/**"

    artifact capitalize($1)+"Component"
    {
        apply "layering"
    }
}

public artifact Framework
{
    include "**/com/hello2morrow/framework/**"    
}
    

In the aspect file "layering.arc" we define our standardized layering. At this point the layer artifacts do not really need to be exposed. That will be needed later when we add connection schemes to our example.

In the main file we use the new template feature. An template is a special kind of artifact that can dynamically create children artifacts out of elements that are matched by the pattern. The pattern must include at least one pair of parentheses so that we can extract the component name and use it as part of the name of a generated artifact. Inside of a template there always is a prototype artifact that uses a string typed expression as its name. '$1" represents the first extracted name part from the matched architecture filter name. We append "Component" to the capitalized extracted name part to form the name of a generated artifact. We explicitly exclude classes of a framework that is mapped to an extra artifact that has been declared to be public so that everything defined in "Components" can use it.

For our example we assume there are 3 components distributed over the following 3 packages:

com.hello2morrow.order
com.hello2morrow.customer
com.hello2morrow.product
    

Then the template artifact "Components" would generate 3 children artifacts named "OrderComponent", "CustomerComponent" and "ProductComponent". All of those would have access to "Framework" because it is a public artifact defined beneath "Components". But on the other hand the three generated components would not be allowed to access each other. Using templates there are currently three ways to regulate dependencies between generated artifacts:

  • No dependency allowed (like in the above example)

  • By marking the prototype artifact as "unrestricted" the generated artifacts could use each other (from default connector to default interface). It is always possible to restrict the default interface and/or the default connector by defining them explicitly.

  • By using connection schemes in combination with artifact classes. That approach will be explained further down.