These reviews
The previous article introduced the second part of the book, “Building Blocks for Model-driven Design,” which distilled some of the core best practices in object-oriented domain modeling into a set of basic building blocks. In a simulated scenario, we used the knowledge of modeling to deal with the requirements of the illusion, and improved the model.
In this chapter, we begin to study the third part of the book. In this section, we will discuss how to assemble the building blocks into practical models to realize their value. Instead of directly discussing esoteric design principles, this section focuses on a discovery process. Valuable models are not immediately available, and the model design needs to be refined repeatedly in order to understand the domain step by step.
Modeling and refactoring
As you can see from the title of Part 3, the focus of this section is on two words: Refactor! We’ve been talking about modeling, so how does refactoring relate to modeling? Before you worry, back to modeling, there are a few things you need to pay attention to in order to successfully develop a model:
- Complex and clever domain models are achievable and worth the effort.
- Such models are difficult to develop without constant refactoring, which requires the close involvement of domain experts and developers who love to learn about the domain.
- To implement and use models effectively requires mastery of design skills.
Simply put: developed models → need constant refactoring.
Classification of reconstruction
Refactoring is redesigning software without changing its functionality. Author Eric divides refactoring into three categories:
- Improve the details of the reconstruction, improve the code readability, standardization.
- The reconfiguration of design patterns and the adoption of mature design patterns can improve the reusability and reliability of code.
- The reconstruction of the representation domain model, which clearly expresses the meaning of the model through code based on the cognition of the domain knowledge.
The three types of refactoring have different depths. The first two are technical redesigns, and only the third refactoring leads to a deeper domain model. This kind of refactoring is something we need to focus on, and its goal is to enable developers to understand not only what the code does, but why, and to link it to conversations with domain experts.
Modeling, like all exploration activities, is unstructured in nature. Follow any path that learning and deep thinking lead you to, and then reframe it accordingly to achieve a deeper understanding.
Deep domain models
Deep domain models can be obtained through continuous reconstruction, so what is deep? The traditional approach to object analysis is to identify nouns and verbs in the requirements document as the initial objects and methods of the system. This approach is too simple to teach beginners how to model objects.
The deep model can cut through the domain representation and clearly express the main concerns and most relevant knowledge of domain experts. The deep model can contain either abstract elements or concrete elements. It is characterized by versatility, ease of use, and strong explanatory power.
From incremental to breakthrough
Each refactoring is a small step forward, and at a certain point it leads to a qualitative leap, which the authors call a breakthrough, which is the most exciting thing about domain-driven design. Sometimes breakthroughs happen when we have MODEL DRIVEN DESIGN and explicit concepts. We have the opportunity to make software more expressive, more diverse, even more than we can imagine. This can bring new features to the software, or it can mean that we can express deeper models in simple and flexible ways, instead of large, rigid pieces of code.
The cost and return of refactoring are not linear. Often, small tweaks bring small rewards, and small improvements add up. Small improvements prevent system degradation, which is the first step in preventing model corruption. Constant refactoring led to a series of rapid changes that resulted in a model that was more responsive to user needs and more realistic. Functionality and explicitness increase rapidly, while complexity disappears.
A little story
After a long winter of reconstruction in New York many years ago, the author and his team finally got a model that reflected some domain knowledge. They are developing software for an investment bank to manage loans.
When a company (like Huawei) needs a huge loan (say, $1bn) to build a factory, the amount is so large that no single lending company can afford it alone. So a number of companies together into a syndicated, syndicated loans. A lending company is also a lender for borrowers and an investor for syndicates.
The author obtained the following model. In commercial banking, a Facility is a commitment made by a company to borrow money, equivalent to a loan amount. A credit card is a type of credit that gives the card holder the right to lend up to a predetermined amount of money when needed and pay it back at a predetermined interest rate. LoanInvestment is a derivative object used to represent the share of an investor in Loan, which is proportional to the share of the investor in the Investment of the Facility.
The shortcomings of this model become apparent when unexpected requirements arise. The credit equity ratio is only a guideline for how much the lender will put in when the borrower Drawdown the loan, and the lender can negotiate with other members of the syndicate to adjust the amount when it actually contributes. LoanAdjustment was added to the model.
Although the model reflects the loan adjustment, the complexity also increases, and more seriously, the complex algorithm reduces the accuracy of rounding, even if the difference is only a few cents, it can cause a very bad customer experience. In the process of reconstruction, the author realized the key of the problem, that is, the shares of Facility and Loan were bound together. In real business, the two do not affect each other and can be changed independently.
For example, the Facility’s limit is $100 million, and the borrower decides to Drawdown a $50 million Loan. The three lenders decided to pay for Facility’s shares.
The borrower then decided to withdraw another $30 million loan, which Company B chose not to participate in and company A took on.
When the borrower repays the $10 million Loan, the principal will be allocated to the lender as Loan shares, not as Facility shares.
On the other hand, when a borrower pays a fee (such as an annual credit card fee), it is allocated according to the Facility’s shares.
The lesson the author learned from this story is, don’t try to make breakthroughs, that will only bog down the project. Often, a breakthrough is only possible after a lot of modest refactoring.