Chapter 9. Epic.Prelude

The Epic.Prelude can be thought of as a preface for your domain model. It provides a set of general purpose models distilled from our experience.

Indeed during some years of domain driven design we faced many different domains and we discovered some common concepts in them (most of which are rooted in theorethical math). After some trials and errors we got a flexible design that is proposed in this module.

[Note]

You could object that while one of the core design targets of Epic is to keep the domain model ignorant of the infrastructure, Epic.Prelude imposes a dependency from Epic itself. You’re right! You should carefully consider pro and cons of such dependency. However, since the Epic.Prelude models are actually usefull we decided to provide them.

[Warning]

From a legal point of view, by referencing Epic.Prelude, your domain model will be subjected to the licencing terms with which you obtained Epic. In other words, if you obtained Epic under the Affero GPLv3, your whole domain must be distributed under the terms of such a license. Note however that, this is already true for the rest of the application built with Epic. Contact the authors to obtain a linking exception if you want to write proprietary code.

Epic itself depends on Prelude’s models, so you will have to include such a library into the runtime environment of applications using Epic.

Interpreting values

Many value objects serves two different purposes: to enforce a set of specific rules and to express such a rule. From the client point of view we can call these two connected responsibilities execution and interpretation.

While this could seem quite abstract, it looks perfectly reasonable when we looks to that particular value objects that work like mathematical functions. Specifications, for example, are predicates that can be both executed, to know whether a candidate satisfy them, or interpreted, translating them into a human readable sentence or into SQL queries. [10]

The need for value objects' interpretation poses a problem to software maintenance: how can we stick to the open-closed principle if we have to interpret an ever growing number of value objects' types?

Most of interpretation techniques rely on the Visitor design pattern and its underlying mechanism known as "double dispatch". However both the classical and the acyclic version of the visitor patterns suffers of a well known limitation: whenever you want to add a new element to the visited class hierarchy you have to change all the visitors.

To address this issue we developed the composite visitor pattern that actually is just an acyclic visitor that replaces inheritance with composition.

The base interface for visitable objects is the IVisitable interface that defines the Accept method. [11]

The first difference from the classical pattern is that Accept takes a visitor and a context for the visit decoupling the visitor from its mutable state. [12]

Moreover, the visitor take controls over it’s own cast to the specialized visitor type [13] through the method AsVisitor<TExpression>(TExpression).

Thus, when the visitor is an instance of a class that extends the CompositeVisitorBase<TResult, TExpression> abstract class, the composition will be able to returns a specialized visitor that designed to interpret that specific expression.

Lets see an example. Whenever the domain model can’t fulfill the user’s requests the UI have to explain them what happens. So you have correctly modeled a hundred types of exceptions that the domain objects throw whenever they need to. However your users works all over the world, talking different languages.

In our sample domain for cargo tracking, all the messages from the domain’s exceptions are already expressive in English. So, for english people we can just print the message, right? So we write a very simple composable visitor that returns the exception message and a composition that use it:

Quite verbose, to just return a string, isn’t it? Now consider that actually we have only control over the domain exception messages. We can’t grant that all exceptions' messages are english! And BTW, we should hide to the user exceptions details from SqlException and the so. But (for the sake of the example) we cannot modify the already deployed ReturnMessages.

Thus we write a new visitor that will intercept all the Exception that are not defined in the domain model assembly, and return a constant message.

Overriding AsVisitor we inform the composition that ConstantMessage cannot handle exceptions defined in the same assembly of Challenge00.DDDSample.Cargo.ICargo.

But hey, what if a domain exception is catched and wrapped in a different, unknown one as InnerException? Since the user speaks the same ubiquitous language of the domain model we want to unwrap the domain exceptions.

However our english messages are still a bit weird. The Challenge00.DDDSample.Location.WrongLocationException extends System.ArgumentException and thus adds to the message provided by the thrower the parameter name. We could fix this with a custom visitor like we did till now, but this time we can write a little more general visitor for the english composition: a Format<TException>. [14]

As you can see, we changed the composition behaviour without any violation to the open-closed principle, by simply adding new visitors to the composition.

To run the visit we simply have to write:

But what about the other languages? Let’s try with italian:

While the example is quite straightforward, you’ve got the point. You can enhance the behaviour of a CompositeVisitor by simply initializing a new visitor.

This turned to be a really powerful tool, able to visit complex expression trees and translate them to nice, human readable, messages or to SQL queries. Moreover, when the class hierachy grows, you don’t need to break existing visitors: you can always extend the behaviour of the composition by adding code, keeping the DLLs already deployed untouched.

Specifying qualities

Coming soon.



[10] A closer look shows that execution is actually a specific kind of interpretation, done by the CLR. Indeed, some stacktraces from Expression<TDelegate>.Compile show that expression tree compilation is done by a visitor producing the executable IL code.

[11] To reduce the effort of implementing the IVisitable interface, a base class exists. Moreover, a set of extension methods is provided for well known class hierarchy such as Exceptions, EventArgs and Expressions.

[12] We want the visitors to be stateless so that we can initialize each of them only once, at application start up, and then use the computation they express in parallel from different threads.

[13] We called this technique "managed cast". It consists into delegating a cast to the object that you want to cast, so that it has control on the casting behaviour according to runtime conditions.

[14] Being such a use case so frequent, Epic provides an even more general version of this class: the SimpleFormatter<TTarget>.