Over a million developers have joined DZone.

Designing a DSL to Describe Software Architecture (Part 3)

Here's part 3 of Hello2morrow's series on describing software architecture with a DSL.

Learn how API management supports better integration in Achieving Enterprise Agility with Microservices and API Management, brought to you in partnership with 3scale

After having covered the basics and some advanced concepts in the previous articles this post will examine the different possibilities to define connections between complex artifacts. Let us assume we use the following template to describe the inner structure of a business module:

// File layering.arc
exposed artifact UI 
{ 
    include "**/ui/**"
    connect to Business 
} 
exposed artifact Business 
{ 
    include "**/business/**"
    connect to Persistence 
 
    interface default
    {
        // Only classes in the "iface" package can be used from outside
        include "**/iface/*"
    }
} 
artifact Persistence 
{ 
    include "**/persistence/**" 
}
exposed public artifact Model
{
    include "**/model/**"
}

This example also shows a special feature of our DSL. You can redefine the default interface if you want to restrict incoming dependencies to a subset of the elements assigned to an artifact. Our layer “Business” is now only accessible over the classes in the “iface” package.

Now lets bring in some business modules:

// File modules.arc
artifact Customer
{
    include "Customer/**" // All in module "Customer"
    apply "layering"
    connect to Core
}
artifact Product
{
    include "Product/**" // All in module "Product"
    apply "layering"
    connect to Core
}
artifact Core
{
    include "Core/**" // All in module "Core"
    apply "layering"
}

Here “Customer” and “Product” are connected to “Core”. We used the most simple way to connect those artifacts which means that all elements in “Customer” or “Product” can use everything in the default interface of “Core”. Since we redefined the default interface of “Business” this is not everything in “Core”. The default interface of “Core” exports all default interfaces of non-hidden nested artifacts which means that the restrictions defined in “Business” are respected by surrounding artifacts.

Nevertheless this way of connecting artifacts does not give us enough control. For example “Product.Model” could now access “Core.UI” – not pretty. That means we need to put a bit more effort into the connection:

// File modules.arc
artifact Customer
{
    include "Customer/**" // All in module "Customer"
    apply "layering"
 
    connect UI to Core.UI, Core.Controller, Core.Model
    connect Controller to Core.Controller, Core.Model
    connect Model to Core.Model
}
artifact Product
{
    include "Product/**" // All in module "Product"
    apply "layering"
 
    connect UI to Core.UI, Core.Controller, Core.Model
    connect Controller to Core.Controller, Core.Model
    connect Model to Core.Model
}
artifact Core
{
    include "Core/**" // All in module "Core"
    apply "layering"
}

Now we are more specific about the details of our connection. Please note that we can only connect to “UI”, “Controller” and “Model” of “Core” because we have marked those artifacts as exposed. Otherwise they would be encapsulated and not directly accessible. The “Persistence” layer is not exposed and can therefore only be used from inside its enclosing artifact.

Introducing Connection Schemes

If you look closely you will find that both connection blocks in “Customer” and “Product” are absolutely identical. Now image you had to connect dozens of artifacts in this way. That would be quite annoying and error prone. To avoid this kind of duplication we added the concept of connections schemes:

// File modules.arc
connection-scheme C2C
{
    connect UI to target.UI, target.Controller, target.Model
    connect Controller to target.Controller, target.Model
    connect Model to target.Model
}
 
artifact Customer
{
    include "Customer/**" // All in module "Customer"
    apply "layering"
 
    connect to Core using C2C
}
artifact Product
{
    include "Product/**" // All in module "Product"
    apply "layering"
 
    connect to Core using C2C
}
artifact Core
{
    include "Core/**" // All in module "Core"
    apply "layering"
}


Now I hope you agree that this is cool. Using connection schemes it becomes possible to describe the wiring between artifacts in an abstract way. That makes it easy to change the wiring if the architect comes up with a new idea or wants to add or remove restrictions.

This concludes this series of articles. If you’d like to try the new language on your own project please request a free evaluation license of Sonargraph-Explorer.

The new language will also be part of our next major release of Sonargraph-Architect, which is planned for November of 2015. If you are currently using Sonargraph 7 it is probably a good idea to make yourself familiar with the concepts of this language. On request we will migrate your existing Sonargraph-7 architecture model for you free of charge provided you have an active support contract.

Please let me know what you think about our new idea? Did we miss something? Would you use that on your project? Feedback is always highly appreciated.

Unleash the power of your APIs with future-proof API management - Create your account and start your free trial today, brought to you in partnership with 3scale.

Topics:
integration ,DSL ,software architecture

Published at DZone with permission of Alexander Von Zitzewitz, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}