Handling Complex Business Scenarios with Domain Modeling — Part 2
We spoke with Alibaba senior technical expert Zhang Jianfei about domain modeling and its benefits and applications. In Part 1 of the article, we talked about to talk about why domain modeling is necessary and its benefits. In this section, we will discuss what a good model is like by looking at a real implementation of domain modeling for bank transfers.
What Is a Domain Service?
Actions in some domains are multiple verbs but don’t look like they belong to an object. They represent an important behavior in a domain, so we can neither ignore them nor merge them into an entity or value object. If you recognize this behavior in the domain, the best practice is to declare it as a service. Such an object no longer has a built-in state. Its role is only to provide corresponding functions for the domain. We often name a service after an activity rather than an entity. For example, in the case of an initial transfer, the transfer is a critical domain concept, but it is between two accounts. Attributing it to the account entity doesn’t make sense. In this kind of situation, it’s more appropriate to use the MoneyTransferDomainService.
Recognizing a domain service mainly hinges on determining whether or not the task fits the below three features:
- Running the service represents a domain concept. We cannot naturally attribute this domain concept to an entity or value object.
- The operation involves another object in the domain.
- Operations are stateless.
How Do We Separate Application Services and Domain Services?
In domain modeling, we generally divide the system into three primary layers: the application layer, domain layer, and infrastructure layer. For details on these three layers, refer to the layer design of my other article on SOFA. We can see that both the App layer and Domain layer have services. How do we separate these services? What kinds of functions should we place on the application layer? What kinds of services should we place on the domain layer?
It is difficult to decide which layer a service should belong to. If we are performing an operation that is conceptually at the application level, we can place the service at this level. If the operation involves a domain object, is indeed related to the domain, and serves the needs of the domain, then it belongs to the domain layer. In summary, behavior that involves important domain concepts get placed on the Domain layer and other non-domain logic code gets placed on the App layer. For example, parameter parsing, context assembly, calling domain services, message sending, and so on. In the case of a bank transfer, for example, the following figure displays the proper service division:
Event Script Implementation of Bank Transfers
In the implementation of the event script, the domain business logic for transferring money between two accounts is written into the implementation of MoneyTransferService, while Account is just the data structure of getters and setters. We call this the anemia model.
The above code should look familiar to you because that is the common method for writing systems. After a review of the requirements, the engineer draws a few UML diagrams to complete the design and then begins to write the business code to the above. This does not require too much effort and is entirely process-oriented. Some of you may say that system functions can be written this way, to which I would reply, “just because you can don’t mean you should.”
To put it bluntly, it is because there are so many coders who stick with
“what works” and don’t seek to minimize the confusion in the application system that the reputation of application development has become corrupted. This is why many application development engineers find the work boring and low-tech. They feel that if they write if-else business logic code all day long, the system is terrible. The work is tedious and boring; there is no growth, and no sense of accomplishments, so it turns into middleware. Oh, to write a JDK, now that’s coding.
Actually application development is neither simple nor boring. Changes to business are much more varied than the underlying infrastructure. Solving problems are no more straightforward than writing the underlying code, but many people choose to do it in a boring way. In fact, there is a more elegant way of doing things, specifically domain modeling. Only by mastering this elegance can you achieve the transition from engineer to application architect. Let’s look again at the same business logic and see how it would be accomplished using DDD.
Implementation of Domain Model for Bank Transfers
If we use an implementation of DDD, Account not only includes account attributes but behavior and business logic, such as the debit() and credit() methods.
The overdraft policy OverdraftPolicy is not just an Enum but is abstracted into objects that contain business rules and adopt a policy pattern.
Domain Service only needs to call the Domain Entity object to complete the business logic.
After restructuring the above DDD, we can divide the logic in the event scripts into Domain Service, Domain Entity, and OverdraftPolicy which satisfy SOLID objects. Before reading on, I recommend that you first try DDD and see the benefits for yourself.
Business Visualization and Configurability
Good domain modeling can reduce the complexity of the application. Also, visualization and configurability primarily help everyone (mainly non-technical people like the product, business, and client management personnel) intuitively understand the system and configure it. Providing a “code free” solution is the main selling point of SaaS software. One aspect we should pay attention to is that visualization and configurability unavoidably add complexity to the system. We have to tread with caution upon caution. It is best to couple the visualization and configuration logic with the business logic as little as possible. Otherwise, the original architecture can get destroyed; so making things more complicated is far from worthwhile.
In the scalable design, I have already discussed how our SOFA architecture supports the needs of different business variations through the design of extension points. Can we go further? We will visualize the domain behavior (also called capability) and extension points, and then use configuration to complete some extension points that do not require code for implementation. For example, taking once again the example of a bank transmission, we can look at the OverdratPolicy extension point. When a new task comes in saying that the overdraft balance can’t exceed 1000, we can make this modification by configuring a ‘rules engine’ rather than writing code.
Therefore, one relatively elegant method I can think of is to use the annotation method to note the capabilities and extensions of a domain, then in the bootstrap phase of the system, use code scanning to bring these capability points and extension points to the central server. Eventually, we can use a GUI to display them, thereby realizing business visualization and configurability. The below is an approximate diagram:
Some of you may question the need for visualizing the process. Here we need to make a clear distinction between the concepts of business logic flow and workflow. Many people confuse these two concepts. Business logic flow is a response to the process of handling a user’s request and is itself business logic. Its arrangement and visualization are not very large. It is nothing more than a visualization of the code logic.
In our SOFA framework, this means using extension points and strategy model to process the branching of the business. When I see the responses of many of our systems at Alibaba to user requests, I find that they use very heavy workflow engines. They turn something simple into something needlessly complex.
Workflow refers to the connection of all of the nodes required to complete a task. Nodes are primarily of two types, automatic and manual nodes, of which each manual node requires user participation, meaning that it responds to a user request. For example, the approval process contains a manager approval node or the staff handling node in the CRM sales process.
Here we can consider using a workflow engine, especially when our system needs to allow the user to customize the process. Here we have no choice but to use a visualization and configurability workflow engine. Furthermore, we’re best off avoiding extra work. Of course, this doesn’t eliminate the possibility of an extremely appropriate use case; I just haven’t seen one yet. If any of you have an example, please let me know so we can talk it over.
Read similar articles and learn more about Alibaba Cloud’s products and solutions at www.alibabacloud.com/blog.