A wide variety of suggestions have been made, which provide food for thought. Here are some other kinds of DSL-based approaches that can be useful some (but not all) of the time...
1. Wrapping API alternatives with a parser for a string-based command
Let's assume you want an application to be able to fetch data using any of several different protocols, such as HTTP, FTP, LDAP, SCP, or reading a file. You could write code to support each of those protocols, but doing that would probably require thousands of lines of code. Instead, you could use "curl", which is available as both a command-line utility and an API. Curl can process an instruction of the form "<protocol>:<protocol-specific-parameters>", for example: "
http://www.example.com/path/to/file". Curl parses the instruction and then calls a protocol-specific API to do the actual work of retrieving the data. By using Curl, a programmer can avoid the need to write thousands of lines of error-prone code, and instead just use a handful of lines of code that pass a stringified instruction to the Curl API. And if that stringified instruction comes from a runtime configuration file, then the application will be flexible in how it retrieves data.
Many years ago, when I was working with CORBA (an object-oriented, remote-procedure-call mechanism), I used a Curl-like approach to provide two simple "stringified instruction"-based APIs that wrapped overly-complex CORBA APIs for: (1) importing and exporting object references; and (2) creating POA hierarchies (a POA was a container of objects, and properties of a POA, such as whether it was single- or multi-threaded, were applied to the objects contained in the POA). Like with Curl, I found my "stringified instruction"-based APIs saved programmers from writing hundreds or thousands of lines of error-prone code, and reading the stringified instructions from a runtime configuration file made applications more flexible. If anyone wants to read details of these API wrappers I wrote, then look at the "Importing and Exporting Object References" and "Creation of POA Hierarchies Made Simple" chapters in this manual:
http://www.ciaranmchale.com/corba-utilities/
2. Using a configuration file to specify how a collection of objects should be created and wired together
In the early days of the X11 Window System, a lot of applications were built using the X Toolkit Intrinsics, which was a C library (providing an object-oriented framework) for creating GUI applications as a hierarchy of specialized types of windows (labels, menu buttons, scroll bars, text editing fields and so on). This framework used a configuration file for specifying attributes (font, font size, background color and so on) of the hierarchical windows in a GUI application. For example, "a.b.c.font: Helvetica" could be used to specify the font in the "c" window which is a child of the "b" window, which in turn is a child of the top-level "a" window. Some clever person decided to add support for two extra attributes in the configuration file: "type" specified the type of a window, and "callback" specified the name of a callback function to be invoked when a particular event (e.g., a mouse press) occurred on a window. He wrote a library that would parse the configuration file and create the entire hierarchy of windows for a GUI application and register callback functions. All that was required was for a programmer to register factory functions for creating each "type" of window, and register a pointer to a C function for each "callback"-type function that might be used in the configuration file. When I discovered this useful library, I realized I could create an application's window hierarchy in, say, 20 lines of configuration rather than in 500 lines of code.
Today, the Spring Framework serves a sort-of-similar purpose: it enables people to use a configuration file to specify how to create and wire-together the objects of an application. I think of the Spring Framework as being a "great idea with a non-ergonomic implementation", so I am not a fan of it, but I am a fan of the basic idea.
The Spring Framework is a *general-purpose* framework for creating objects from configuration, while the X11 utility library I discussed above is a *domain-specific* framework for creating objects from configuration. I have implemented several domain-specific frameworks of this type and have found them useful. If anyone wants to read about one of them as an in-depth case study based on the Java Messaging Service (JMS), then I suggest you visit
http://www.config4star.org, read chapters 2 & 3 of the "Getting Started Guide" (for an overview of the syntax and API of the configuration parser), and then read chapters 7 to 11 of the "Practical Usage Guide".
Many years ago, I read a book about enterprise design patterns. A large subset of the book was devoted to discussing patterns associated with one-to-one, one-to-many and many-to-one message flows, and the thought struck me that a DSL might be developed that would read a specification of the desired message flows and create the required objects (using a particular middleware technology), complete with quality-of-service properties specified in the configuration file. The same thought occurred to me when I read (badly written) documentation for the open-source ZeroMQ middleware project.
Final thought... Simon mentioned it is desirable to separate business logic from infrastructure/libraries/generic bits. People have found it useful to also maintain a separation between business logic and the generation of HTML pages in a web server, and various template engines have attempted to provide such a separation. Terence Parr (most famous for designing the ANTLR parser generator) wrote an interesting paper called
"Enforcing Strict Model-View Separation in Template Engines" that compares his StringTemplate engine against other template engines, and explains why most template engines fail in their goal of enforcing separation. Here is a link to the paper for anyone interested:
https://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf