The keynote talk at the UML conference a couple of days ago was by Krzysztof Czarnecki, on Generative Programming (title of his book with Ulrich Eisenecker, from 2000). Generative Programming is about using Domain-Specific Languages --- from simple wizards through to complex modelling languages --- to create software as part of a software product line. 

Czarnecki includes in his idea of DSLs, everything from the simplest wizards and configuration tools, to complex models. He came along to my tutorial, and we made an interesting comparison of methods for creating a DSL:

  • Feature Diagrams are a quick informal method for a high-level, hierarchical view of requirements. You can highlight the alternatives and optional bits, and it's those that your DSL needs to express.  E.g.:
    Car (needs Body, needs Engine (either {Electric, Gasoline} ), optional PullsTrailer)
    The needs bits are assumptions of your domain and (therefore) your implementing framework; so your car-specific language, which is about distinguishing one car from another, doesn't need to express them.
  • Model variation analysis is about pinpointing the variable parts of a generic model of the domain. It's a bit more formal, but yields more detail, and works better where there is more variability, where the DSL is expected to be more complex. Here's how it works:
    • Make a model of your domain, with at least a class diagram. (I like to put in some constraints (business rules) and use-case definitions too, defining the use-cases by postconditions -- that is, in terms of the changes they bring about in the attributes and associations.)
    • Annotate each attribute and association with a variability measure -- that is, how often it changes. The boolean attribute Engine.isRunning changes often during the life of a Car; whether the car has two doors or more doesn't change very much -- probably only between one Car and another; whether a Car has an Engine doesn't ever change. 
      • If you've written constraints and use-cases, you can annotate each clause with variability too.
    • Each range of variability you've identified can correspond to a language. Rapid-changing features correspond to an operational language (of controlling the car, monitoring its engine etc). Slow-changing features correspond to a language of car design (defining how many doors a car has, etc).  When you design your language, check that each of the features in a particular range is covered.
    • A separate question -- not to be confused with the previous one -- is how the language is executed. A language of slow-changing features can be put through a generator -- for example to create a car and the software that controls the engine. Alternatively, you can make a very generic framework that can interpret the language on the fly -- so that even slow changing features can be changed in a running system (bit difficult with numbers of car doors, though....)
    • Another separate question comes from the fact that you probably have several notations for different aspects of the system -- different stakeholders' viewpoints, and different horizontal aspects like security, loggging etc. You might either combine them by AOP once you've generated code from your DSL; or by composing the models beforehand. One reason for doing it beforehand is that some of your generated artefacts can be in different languages -- configuration files, icon gifs etc.