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 Wortmann impressively 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.