- Ingredients of Effective Modeling
- Knowledge Crunching
- Continuous Learning
- Knowledge-Rich Design
- Deep Models
Knowledge-Rich Design
The kind of knowledge captured in a model such as the PCB example goes beyond “find the nouns.” Business activities and rules are as central to a domain as are the entities involved; any domain will have various categories of concepts. Knowledge crunching yields models that reflect this kind of insight. In parallel with model changes, developers refactor the implementation to express the model, giving the application use of that knowledge.
It is with this move beyond entities and values that knowledge crunching can get intense, because there may be actual inconsistency among business rules. Domain experts are usually not aware of how complex their mental processes are as, in the course of their work, they navigate all these rules, reconcile contradictions, and fill in gaps with common sense. Software can't do this. It is through knowledge crunching in close collaboration with software experts that the rules are clarified, fleshed out, reconciled, or placed out of scope.
Example
Extracting a Hidden Concept
Let's start with a very simple domain model that could be the basis of an application for booking cargos onto a voyage of a ship.
Figure 1.8.
We can state that the booking application's responsibility is to associate each Cargo with a Voyage, recording and tracking that relationship. So far so good. Somewhere in the application code there could be a method like this:
public int makeBooking(Cargo cargo, Voyage voyage) { int confirmation = orderConfirmationSequence.next(); voyage.addCargo(cargo, confirmation); return confirmation; }
Because there are always last-minute cancellations, standard practice in the shipping industry is to accept more cargo than a particular vessel can carry on a voyage. This is called “overbooking.” Sometimes a simple percentage of capacity is used, such as booking 110 percent of capacity. In other cases complex rules are applied, favoring major customers or certain kinds of cargo.
This is a basic strategy in the shipping domain that would be known to any businessperson in the shipping industry, but it might not be understood by all technical people on a software team.
The requirements document contains this line:
-
Allow 10% overbooking.
The class diagram and code now look like this:
Figure 1.9.
public int makeBooking(Cargo cargo, Voyage voyage) { double maxBooking = voyage.capacity() * 1.1; if ((voyage.bookedCargoSize() + cargo.size()) > maxBooking) return –1; int confirmation = orderConfirmationSequence.next(); voyage.addCargo(cargo, confirmation); return confirmation; }
Now an important business rule is hidden as a guard clause in an application method. Later, in Chapter 4, we'll look at the principle of LAYERED ARCHITECTURE, which would guide us to move the over-booking rule into a domain object, but for now let's concentrate on how we could make this knowledge more explicit and accessible to everyone on the project. This will bring us to a similar solution.
-
As written, it is unlikely that any business expert could read this code to verify the rule, even with the guidance of a developer.
-
It would be difficult for a technical, non-businessperson to connect the requirement text with the code.
If the rule were more complex, that much more would be at stake.
We can change the design to better capture this knowledge. The overbooking rule is a policy. Policy is another name for the design pattern known as STRATEGY (Gamma et al. 1995). It is usually motivated by the need to substitute different rules, which is not needed here, as far as we know. But the concept we are trying to capture does fit the meaning of a policy, which is an equally important motivation in domain-driven design. (See Chapter 12, “Relating Design Patterns to the Model.”)
Figure 1.10.
The code is now:
public int makeBooking(Cargo cargo, Voyage voyage) { if (!overbookingPolicy.isAllowed(cargo, voyage)) return –1; int confirmation = orderConfirmationSequence.next(); voyage.addCargo(cargo, confirmation); return confirmation; }
The new Overbooking Policy class contains this method:
public boolean isAllowed(Cargo cargo, Voyage voyage) { return (cargo.size() + voyage.bookedCargoSize()) <= (voyage.capacity() * 1.1); }
It will be clear to all that overbooking is a distinct policy, and the implementation of that rule is explicit and separate.
Now, I am not recommending that such an elaborate design be applied to every detail of the domain. Chapter 15, “Distillation,” goes into depth on how to focus on the important and minimize or separate everything else. This example is meant to show that a domain model and corresponding design can be used to secure and share knowledge. The more explicit design has these advantages:
-
In order to bring the design to this stage, the programmers and everyone else involved will have come to understand the nature of overbooking as a distinct and important business rule, not just an obscure calculation.
-
Programmers can show business experts technical artifacts, even code, that should be intelligible to domain experts (with guidance), thereby closing the feedback loop.