Perspectives on DSLs:
Software Architecture
This most recent post on the
relationship of DSLs to other software engineering fields is motivated by a
recent interactions I have had with software architects. They seemed to
consider the use (or not) of DSLs more or less irrelevant to “the architecture”.
It’s not the first time I have encountered this position, so I thought I’d
write about it next.
DSLs specifically for
software architecture
First of all, there are DSLs
explicitly designed to express architecture. Examples abound, they include the
various Architecture Description Languages, SysML,
to some degree UML, but also more domain-specific ones such as AUTOSAR or Franca.
The idea is described in a
paper I wrote long ago, called Architecture as Language. As this paper shows, using a DSL
(instead a more generic architecture description language) allows you to
represent domain-specific architectural concepts first-class, and thus more
semantically rich. Just having the language, and having the ability to evolve
it, lets you think much more meaningfully about your architecture.
The modes are often the basis
for early analysis regarding performance, safety and security. This can improve
these “ilities” and hence should be of interest to architects. Architecture
models are also often used for generation of skeletons or interfaces, which
serve as the basis for the implementation. While this notion of separating the
design from the implementation is often frowned upon in agile circles, really
large systems (such as the electronics in a car, ship or aircraft) can benefit
from this slightly top-down-ish approach to development.
The connection between DSLs
and architecture is rather obvious: the models described with these languages
form the “master representation” of the architecture in terms of thinking,
analysis and documentation; the models also form the blueprint for the
implementation.
Similarly, the ability to
express interfaces, components, instantiation and connectors in mbeddr’s components makes
architectural abstractions first-class citizens in the implementation,
something that is hard to achieve with C alone. As our paper on the Smart Meter demonstrates, this leads to a
completely different software architecture compared to an implementation in
plain C. Andreas Wortmann reports a
similar experience from making the concepts in ESA’s standard
satellite software architecture available as mbeddr extensions: the
architecture becomes more prominent, “more first-class”, and hence plays a much
more important and explicit role. It’s not just something someone drew up in
Powerpoint.
On the other hand: it is true
that one can use DSLs and models and code generation without any consequence
for the structure of the target system, both high-level (architecture) and
low-level (code structure). And this is often a feature, not a bug: no matter
how strange the libs and frameworks and idioms one must use on the target
platform, the DSL lets you abstract over them, provided you are willing to
write a generator. Early versions of J2EE were a primary candidate for this
approach because it was very verbose, as the libs and languages used for
developing embedded systems, where performance and resource constraints might
prevent devs from adding runtime abstractions. The approach also makes sure you
can comply to any architecture guidelines that you might have to conform to,
such as “microservices based on Spring Boot””.
DSLs for implementing
business logic
In the case above we have
used DSLs to represent architectural concepts — the connection between DSLs and
architecture is relatively obvious. In this section we look at the use of DSLs
for business logic, i.e., the stuff that goes “inside” architectural building
blocks.
As an example, consider the
case of payroll calculation. A DSL might be used to concisely describe the core
data structures, validations and calculations. From these models, one can
generate Java code, POJO classes that are then used inside a Spring-based
microservice. In this example you could argue that from an architectural
perspective, it does not matter whether this “calculation core” is written
manually in Java or generated from a model; in both cases it makes sense to
separate the core business logic POJO from the service.
However, it’s not so simple.
Let’s for example consider the case where we don’t generate code from the
models at all, but instead just serialize the model to, say, JSON, and then use
an interpreter to run the business logic in the service. In this case we would
have a single, generic service implementation for all services. That’s a pretty
big architectural impact! If you consider a phone app instead of a cloud
service, an interpreter might be the only way how you can get updates to your
users quickly (because a new binary might get stuck in the app store review
process).
Alternatively, if we stay
with code generation (and not interpretation) for the internals of these
service components, then we might also be able to generate some of the service
shell, including schemas for the data and interfaces. Of course, we could say
that there is no impact on the software architecture: we generate exactly the
same stuff that would alternatively be handcoded. However, that’s also too
simple. First, automation accelerates development, and hence the flexibility of
changing and evolving the architecture or the underlying technologies. Such a
flexibilization is a ajor architectural concern! In addition, the generators
embody patterns, idioms and best practices and ensure their consistent use in
the code (with the resulting benefit for non-functional properties). The paper by
Wortmannimpressively demonstrates both these aspects. Again, this is
something that architects should care about.
Tool Architecture and DSLs
This one is the elephant in
the room. The overall architecture of a system is not just the architecture of
the software that ultimately runs in the data center or one the phone or
wherever. Architecture also concerns the way how the business logic gets into
the software. This is especially crucial for systems that contain a lot of
complicated — and potentially rapidly evolving — domain logic: data structures,
checks, tables, rules, calculations, semantic versioning and such. How do we
expect to deal with this?
· Do we distinguish between domain experts and
programmers or do we expect developers who do both?
· If we have the separation, do the domain
experts write documents which are then implemented by programmers? Is this an
efficient way to exchange knowledge and get it into software?
· Alternatively, do we want domain experts to
create executable models using a DSL?
· If not, how do we want to isolate the domain
logic from the implementation to decouple the lifecycles of the two?
· How do we address the testing and debugging
of this domain logic?
· What does all of this mean for the roles and
processes regarding business logic development?
I think all of this is
architecture! It is not the architecture of the target software system; you
could call it tool architecture or development architecture. In any case, an
architect who thinks holistically about a system should care about all of this.
Wrap Up
Ultimately, the use of DSLs
opens up new alternatives regarding the processes you use to develop software.
Your domain experts can contribute directly (as in: write executable
code/models), and not just by writing prose in requirements documents or user stories.
Their work can be decoupled completely from the technical concerns; this is
handled by the platform and the generator/interpreters. This, in turn, makes
your teams smaller while still giving them something concrete and formal to
facilitate their integration (the models). The technical team can make changes
to the implementation independent of the domain logic. This decoupling, and the
resulting increased flexibility on both sides can seriously boost business
agility and help with the reduction of technical debt.
This should therefore be a
major concern to every software architect.