Example of using DSL (Domain Specific Languages) in a real project

    DSL (Domain Specific Languages) - languages ​​specific for solving problems of any domain (as opposed to general-purpose languages ​​such as Java or C #). There is a more detailed description and examples on Wikipedia , but I want to write about how it is quite easy to embed my own DSL based on the Boo language in an application (C #) .

    We had a task - a service for online data synchronization from Active Directory to the corporate information system XXX (users, organizational units, groups, etc.). The main obvious difficulty here is that each specific enterprise can have its own AD scheme (somewhere for departments they can use OrganizationalUnit, somewhere - groups, etc.), as well as their own classes to represent departments and employees in the XXX system (its properties and logic, and at the level of the synchronization service we don’t know anything about them, only their base classes), and even different rules for synchronization (somewhere you need to synchronize posts, somewhere not).

    Offhand, you see a solution with the task of mapping AD to XXX via XML, such as this:


    But here a lot of problems come out - the need to implement your own rule parser (which creates the rule objects in XML) and the rule interpreter (which fulfills them), limited XML capabilities (such as specifying type conversion or a slightly more complex calculation, for example, adding to the name login prefix with domain name) etc. The main advantage of XML is that it’s easy to attach a GUI to such rules to set them, and the task is familiar and has been solved more than once.

    These problems prompted us to think that the task fits the type of tasks that are well solved using DSL. Since we did not have much experience and knowledge in this area, in the beginning we did a little research and made a couple of prototypes. We were mainly based on the book DSLs in Boo - Domain-Specific Languages ​​in .NETand the Rhino DSL framework from the author of the book.

    Here are some examples of DSL from the book:

    DSL for setting authorization rules:


    DSL for setting modular software delivery rules and module requirements (+ here is an example of implementing a DSL editor with Intellisense and syntax highlighting based on the SharpDevelop code editor):


    All these are examples of “internal” DSLs that are hosted in the Boo language, that is, the above code is legal Boo code that can be compiled into regular clr classes or interpreted. Boo was chosen because it has rather convenient syntax for DSL, rich possibilities for expanding the syntax (you can write macros, such as macros in C, but they manipulate not with text, but with a syntax tree) + it is open and it has a convenient compiler API into which you can easily embed your actions.

    Especially convenient is the fact that Boo gives us the output of the usual .Net classes. As a result, after processing the DSL files from Example 1, we get the Rule type, which has a method, for example, Evaluate (user), when called, the Boo code will be executed, which will give the result whether the user needs to be authorized. That is, from the point of view of the developer and the application, everything looks absolutely transparent, as if the rules that are written in DSL are written just like regular code. Very cool.

    Our result


    Here is an example of a rather complicated rule that synchronizes users and OrganizationalUnit from AD to departments and employees of the corporate system XXX, while also taking into account the hierarchy of departments: By the way, these rules are transparently debugged in the studio without installing any plug-ins in it.




    Internal implementation


    Actually, in fact, everything is simple here and everything is done by the standard Boo syntax. A rule is compiled in the context of our class, in which there are methods with the names rule, for_type, etc. So the

    for_type user code
    is actually just a call to the for_type method with one string parameter “user”. And inside the method, we simply write the passed value into the rule property and in the future we can manipulate it.

    Entity and ldap are properties of the rule with types that implement the special Boo interface - IQuackFoo, which allows them to implement dynamic typing (such as IExpandoObject and dynamic in C #). Therefore, when ldap.GivenName is written in the rule, the ldap class actually looks for the attribute named “GivenName” in the user’s attributes and returns its value. Similarly with entity.

    The only syntactically tricky place is the expression on entity.Sid == ldap.objectGUID. Here on is a meta-method that works out during compilation and replaces the syntax tree of the entity.Sid == ldap.objectGUID expression with another. Specifically, here we simply parse from the tree the names of the properties by which the key is given and remember them. The meta-method looks like just a C # method that is marked with a special attribute and which takes a syntax tree as input and returns a new tree. Everything is simple :).

    For details on the implementation, ask if it’s worth reading DSLs in Boo and picking up Rhino DSL, everything is very well described there, and Rhino DSL allows you to connect a DSL project to Boo in a couple of minutes.

    Also popular now: