Handling Complex Business Scenarios with Domain Modeling — Part 1
Do you still use process-oriented code in an object-oriented language for your business? Does your business suffer from complex business logic?
Oftentimes business requirements and organizational changes are much more varied than the underlying technological infrastructure. Packaging these changes requires good business understanding, abstraction, and modeling capabilities.
Today we invite Alibaba senior technical expert Zhang Jianfei to talk about why domain modeling is necessary, what a good model looks like, and how to build it.
Why Is Data Modeling Necessary?
There is no silver bullet in the world of software. There is no right or wrong between business scripting and the domain model. The key is to determine which is appropriate. In fact, CQRS is a combination of event script and domain model, because in query and report situations, using the domain model will often complicate things that should be simple. Here you can apply Occam’s razor to shave off the domain layer and directly access the infrastructure. I am personally opposed to over-design.
That’s why, in a simple business scenario, I strongly recommend using event scripts. They are simple, intuitive, and easy to use. However, for complex business scenarios, you will not be able to take this course of action, because once the business becomes complex, event scripts quickly become difficult to deal with and easily result in code pancaking. The speed and complexity of system corruption will increase exponentially.
Currently, the most effective approach is domain modeling, because the domain model is object-oriented, encapsulates business logic, and enhances the cohesion and reuse of objects because of the use of Ubiquitous Language. This gives explicit expression to hidden business logic, making complex governance possible. Talk is cheap, so let’s get into the example of a bank, compare event scripts with the domain model, and see which is more advantageous.
Benefits of Domain Modeling
- Encapsulation: We can encapsulate Account related operations in the Account Entity, improving cohesion and reusability.
- Polymorphism: OverdraftPolicy with policy mode (a typical application of polymorphism) improves code extensibility.
Explicit Business Semantics
- Common language: “One team, one language” using the model as the backbone of the language. Make sure that the team uses the same language in all internal communication, code, drawing, writing, and especially speaking. For example, account number, transfer, overdraft strategy; these are all very important domain concepts. If these names are consistent with our daily discussions and the description in PRD, it will significantly improve the readability of the code and reduce the mental gymnastics required to understand it.
- Explicit: This refers to extracting hidden business logic from a pile of if-else statements, use language to name them, write code, expand it, and then turn it into an explicit concept. For example, the crucial business concept of the “Overdraft Strategy.” According to the way in which we write the event script, its meaning is all over the code logic but is never explicitly dated. Anyone looking at the code would be flabbergasted; however, the domain model uses a strategic method to abstract it, not only increasing the usability of the code but making it much more scalable.
How Do We Implement Domain Modeling?
The topic of domain modeling is too big, and there are already many lengthy articles and books on the subject. For example, there is a deep analysis of grammar and syntax, which in my opinion is somewhat tedious. A good model should be based on a deep understanding of the business. If the understanding is not up to snuff, then you can’t properly analyze statements and produce an acceptable model. In my own experience, modeling is a process of constant iteration, so we can begin, take two modeling steps to grasp some core concepts, then write code to verify and run it. We can check to see if it works smoothly. If everything is smooth then there aren’t any problems; otherwise, we need to check whether or not we can adjust the model, then continue to work according to the progress of the project and our understanding of the business. Then we continue with further iterations.
So what is the two-step modeling method? It only takes two steps to create the model; first, we find nouns and verbs from the user stories, and then use the UML class to draw the domain model. Pretty streamlined, right? Just because something is streamlined doesn’t mean that it’s simple. This is often the holy grail for business architects and system analysts.
For example, if we design an intermediary system, then a typical user story might be “The user is looking for work, and the agent asks him to leave his phone number to be contacted if there is a job available.” The keyword here is likely the domain object we need. The user is a job seeker, while the phone number is a property of job seeker. The Agent contains two keywords in agency and agent, and the job is a keyword domain object. Notifying this verb tip, we use observation mode. Then, sort out the relationship between the domain objects. A job seeker can apply for multiple job opportunities. Also, multiple job seekers can also apply for the same job, which is an M2M relationship, and intermediary companies can include numerous employees, which is an O2M relationship. In a simple scenario like this, this kind of modeling is sufficient.
Of course, our business scenarios are always more complicated than this, and not all nouns are domain objects; they could be properties. Likewise, not all verbs are methods; they could be domain objects, so we need to use specific solutions for specific problems. This process of problem-solving requires that we have an excellent understanding of the business, abstract ability, and modeling experience (we know now why the job model of all those companies emphasize that technicians have a keen ability to understand business and abstract). For example, in most situations, price and inventory are just properties of an order and a product; however, at Alibaba, the complexity of price calculation and inventory reduction would make your head spin. Therefore, as an e-commerce platform, treating price and inventory as separate domains is a matter of necessity.
Moreover, modeling is far from a one-time job. Our view of the overall picture will always change as business changes, and our understanding of it improves, so iterative restructuring is unavoidable; making Modeling Agile, is crucial.
Model Unification and Model Evolution
The process of modeling is much like feeling out an object. People with different backgrounds and perspectives looking at the same object might have a different understanding of it. For example, two blind people could touch the trunk of an elephant. One could find it similar to a snake (alive and mobile); whereas the other person might find it to be more like water hose (sprays water). So it is difficult to develop common understanding.
Both parties have difficulty accepting the other’s model because it doesn’t fit their own experience. In fact, they need a new abstraction. This abstraction needs to combine the “liveness” of the snake with the “water spraying” of the fire hose. This abstraction should also exclude some of the inaccurate interpretations of the previous two models, such as fangs or that ability to be rolled up and put in the back of a truck. This is how we can unify the models.
The only thing in the world that doesn’t change is the change itself. Models and code also need constant reconstruction and refinement. After each refinement, developers should have a clearer understanding of the domain. This makes breakthroughs in understanding possible.
Afterward, a series of rapid changes result in models that are more user-friendly and more realistic. Its functionality and descriptiveness increase rapidly, and complexity disappears.
This breakthrough requires us to have a deeper understanding of the business and thought processes behind it. Then we build up the courage and ability to restructure. Courage comes when the project deadline is tight, and you have to decide if you can even dare to restructure. Ability indicates whether or not you have a complete CI to guarantee that restructuring the code won’t break other existing business logic.
Let’s look again at the example of account transactions that kicked off this piece. Let’s consider complex case where we have to support cash, credit card, Alipay, Bitcoin, and other payment channels. The restrictions for each channel are different, and we have to support one to many transactions. In this case, we can’t possibly still use a transfer(fromAccount, toAccount) method. Instead, we need to abstract a specialized domain object called Transaction so that we can better express the business. The evolution of this object is as below: