DSLs in Visual Studio
Visual Studio 2005 has several graphical domain-specific languages integrated into it. These are the Distributed System Designers, which come with Visual Studio 2005 Team Edition for Software Architects, and the Class Designer which comes with Visual Studio 2005 Standard Edition and later. These designers are built on an earlier version of the DSL Tools; the current version is based on this earlier version and has evolved separately. The two versions are incompatible, which means that the DSL Tools cannot be used to extend the integrated designers.
Nevertheless, these designers illustrate very well some of the motivations for using domain-specific languages. Let's look at a simple example, using the Application Designer. This is a tool for modeling applications in distributed systems, with a particular emphasis on the endpoints that the applications implement and use, so that the user can wire the applications together into more complex systems. Figure 1-14 shows a simple design consisting of a Windows application, called InvestmentCalculator, that talks to an endpoint called StockPrices, which is implemented as a web service by an ASP.NET web application called StockPriceApplication. The StockPrices web service is shown as a provider endpoint on the StockPriceApplication node and is wired to a corresponding consumer endpoint on the InvestmentCalculator node.
Figure 1-14 An application diagram
Having created this design and chosen the implementation language, the Application Designer can generate the skeleton of an implementation for it using standard code templates installed with Visual Studio. The diagram context menu item "Implement All Applications ..." causes the generation of two new projects in the solution, including the files needed to implement the solution, as shown in Figure 1-15. Implementing the application by generating these files like this requires much less work than does creating these files by hand. This is one clear benefit of defining a DSL—we can more quickly generate code that would be tedious and error-prone to write by hand.
Figure 1-15 The generated solution
It's interesting to look into this solution and see where the name of the web service—StockPrices—appears. There are several places, in fact, including:
- The name of the file StockPrices.cs.
- The body of the generated file StockPrices.cs, containing the following code, which mentions StockPrices as the name of the class in the Name parameter of the WebServiceBinding attribute and in the Binding parameter of the SoapDocumentMethod attribute.
namespace StockPriceApplication { [System.Web.Services.WebServiceBinding(Name = "StockPrices", ConformsTo = System.Web.Services.WsiProfiles.BasicProfile1_1, EmitConformanceClaims = true), System.Web.Services.Protocols.SoapDocumentService()] public class StockPrices : System.Web.Services.WebService { [System.Web.Services.WebMethod(), System.Web.Services.Protocols.SoapDocumentMethod(Binding="StockPrices")] public string GetPrice(string Symbol) { throw new System.NotImplementedException(); } } }
- The name of the file StockPrices.asmx.
- The body of the file StockPrices.asmx, containing the following template, which mentions StockPrices as a class name and a file name.
<%@ webservice class="StockPriceApplication.StockPrices" language="c#" codebehind="~/App_Code/StockPrices.cs" %>
- The two SDM (System Definition Model) files. These are XML files that describe operational requirements for the applications and can be used to match these requirements against the operational facilities provided by a data center. This is not the place to go into the details of these files; suffice it to say that they both contain references to the service called StockPrices.
- The web reference in the InvestmentCalculator application, which contains a URL such as http://localhost:2523/StockPriceApplication/StockPrices.asmx?wsdl.
- The app.config file for the InvestmentCalculator application, containing the following section, which includes a reference to the filename StockPrices.asmx as well as the name StockPrices embedded in the longer name for the setting.
<applicationSettings> <InvestmentCalculator.Properties.Settings> <setting name="InvestmentCalculator_localhost_StockPrices" serializeAs="String"> <value> http://localhost:2523/StockPriceApplication/StockPrices.asmx </value> </setting> </InvestmentCalculator.Properties.Settings> </applicationSettings>
Now imagine that you want to change the name of the web service. Instead of StockPrices, you'd prefer to call it StockValues. Working in a modern coding environment, this should be a simple refactoring operation, such as the ones available from the "Refactor" menu in the code editor. But unfortunately, opening the StockPrices.cs file and using the "Refactor" menu will not have the desired effect, because many of the occurrences of the name StockPrices are not in code.
However, changing the name from StockPrices to StockValues on the Application Designer diagram does have the right effect. All of the references within the StockPriceApplication project are updated immediately, including the filenames and all of the references in the list above. At this point, the consumer endpoint on the InvestmentCalculator project is marked with a small warning symbol to indicate that it is referring to something that has changed; the web reference in the InvestmentCalculator project has been removed, and the app.config file no longer contains any reference to StockPrices. Selecting the "Implement" option from the context menu on the endpoint causes the web reference, app.config, and SDM files to refer to the new name. By using the DSL, the operation of changing the name has been reduced from a time-consuming and error-prone combination of multiple manual edits to a simple two-step procedure carried out at the appropriate level of abstraction.
You may ask what happens if you change the name of StockPrices in just one of these generated artifacts. Well, by doing that you have invalidated your solution. In general, it is difficult or impossible for a tool to solve all of the possible round-tripping conundrums that could be created if you allow complete freedom to edit any artifact at any time. In this particular case, you are allowed to insert your own code into the body of the GetPrice() method, and that code will be preserved if the endpoint or operation name is changed in the model. But if you manually change the name of the class or method itself in the code, you have effectively broken the relationship between the code and the model, and future changes will not be synchronized. We return to the general problem of keeping models and artifacts synchronized in Chapter 8.
We can summarize the qualities of the Application Designer, which are qualities that any well-designed DSL should possess, as follows:
- It is a sharply focused tool for a specific task.
- The model corresponds closely to the domain being modeled, and the transformations required to generate code and other artifacts are simple.
- Because of these simple transformations, the round-tripping problem becomes tractable.
- The artifacts associated with the language are all files and can be maintained in a source-control system, and the tool is engineered so that it works effectively in this environment.
- The interactive user experience on a modern computer is rapid and intuitive.
- The files manipulated by the tool are user-readable text files, using published formats based on XML.