Book Excerpt and Review: Applying Domain-Driven Design and Patterns with Examples in C# and .NET
In the chapter "Preparing for Infrastructure", Nillison discusses some topics such as persistence ignorant (PI) classes and querying.
So let's assume we want to use PI. What's it all about? Well, PI means clean, ordinary classes where you focus on the business problem at hand without adding stuff for infrastructure-related reasons. OK, that didn't say all that much. It's easier if we take a look at what PI is not. First, a simple litmus test is to see if you have a reference to any external infrastructure-related DLLs in your Domain Model. For example, if you use NHibernate as your O/R Mapper and have a reference to nhibernate.dll, it's a good sign that you have added code to your Domain Model that isn't really core, but more of a distraction.Applying Domain-Driven Design and Patterns does not begin like your typical patterns and practices book. The background chapters alone cover over a hundred pages and cover a wide variety of topics of general interest.
What makes this book particularly refreshing is the level of honesty in conveys. Unlike some books of this sort that focus on the merits of the chosen techniques and flaws of the others, this book gives a fair treatment to everything it touches on. There is a heavy emphasis on the flaws of each technique discussed, even those of Domain-Driver Design, and ways to minimize their impact.
Overall the book has an informal tone that makes reading it enjoyable. When the author says something controversial or just plain odd, it is often followed by a "Note" section. These sections, which appear to be in response to reviewer comments, have a conversational tone that addresses issues and assumptions outside the normal flow of the text. While not essential to the material, they serve to deepen the reader's understanding of not only the text itself but also why Nilsson came to those conclusions. Nilsson also uses these to point out instances where he has a bias or lacks knowledge in an area.
As mentioned before, the first section is a review and discussion of background information. Topics touched on include the use of models as incomplete abstractions, design patterns, and an especially interesting way to combine Test Driven Development with Refactoring.
In the design patterns chapter, the author introduces each pattern by considering several viable solutions for the problem the pattern is meant to address. Viable is key word here, as each option is suitable at times.
For the state pattern, Nilsson starts with the simplistic design in which each method of the class independently verifies the state is correct and performing any necessary transitions. He then goes on to show how this logic can be centralized by using an internal function containing a switch block or via a table containing the current state, action to be performed, and the next state. After considering these alternatives, the author then introduces the State pattern. The State pattern uses an internal class to represent each state with the appropriate methods being overridden, which in theory reduces code duplication. While not comprehensive, the author touches a few more design patterns at various levels including the domain and the overall architecture.
The next section begins the design process formally. First a list of requirements is presented. The requirements are not the typical, nonsensical case studies about elevators or home security systems we see in books like Pressman's Software Engineering. They are real talking points and look like something one would really encounter when building a system. Even more impressive, the requirements seem to be chosen to emphasis they flaws in Domain-Driven Design.
In his introduction he notes that using domain driven design is a move away from a database-centric perspective. He argues that this trades efficiency for maintainability. Database design isn't ignored completely, but it takes a backseat to the domain design.
Each requirement is first discussed with a combination of written discussion, hand-drawn UML sketch, and a test case expressed as code. Nilsson uses the hand-drawn sketches to emphasize that the test code is "the most important representation of the real model." The UML is essentially a stepping stone to the real design documentation and not an artifact in and of itself.
The author suggests that a basic UI should be examined early in the design phase. In this way the domain design can be tested against its planned use. As the focus is on the domain itself, very little time is spent on the UI itself, which can be sketched out with pencil and paper. The goal is to demonstrate that the domain API can be used without the UI programmer needing to know about the inner details of the persistence model.
Identifying instances where design patterns are used incorrectly is also important. In chapter five, Nilsson demonstrates test driven development and refactoring by reconsidering an earlier design decision. As originally designed, the author used a factor method to create order objects. Having realized that the factory method didn't actually add anything to order class, he refactored the code to use a simple constructor. Then he used the test suite to verify that the change didn't break any code.
Because Domain-Driven Design emphasis separating the domain objects from the database, the issue of database persistence isn't fully addressed until the third section. That isn't to say it is ignored up until that point, as nearly every discussion in the previous section seems to include mention of the persistence issues that will eventually need to be addressed. A wide variety of implementation options are discussed first, then NHibernate severs as a concrete example.
The final section is a collection of essays by various authors that touch on Solution Orientated Architectures (SOA), Inversion of Control (IoC), Dependency Injection, Aspect Orientated Programming (AOP), and User Interfaces. Each is covered just enough to give the reader a good feel for what it is and whether or not they should pursue it further.
My one complaint about the book is a lack of coverage in the area of user interfaces. User interfaces are just as important as the persistence layer, but are hardly mentioned by the principal author while the latter is a constant concern. Because they are not tied into the rest of the text, the essays manage to side-step the requirements of the main text and the hard questions that arise from them. The final result is the same sort of ill-considered, impractical advice the Nilsson so skillfully avoided throughout the first nine chapters.
A follow up book that addresses applying Domain-Driven Design in the context of both web based and rich client applications is definitely in order.
In conclusion, this book doesn't offer any golden hammers or silver bullets. It does offer a good point for application design, plenty of warnings about when it doesn't work, and several options on how to handle those situations.
Mike Amundsen May 29, 2015
Ben Linders May 28, 2015