I have a lot in common with the author in terms of code quality and design. I would like to quote some of them and add my personal reflections (the link file is long and worth reading).
Projects with too much duplicate code
On the one hand, the repeated logic is embedded in the method and not extracted independently. On the other hand, RD does not know that the implemented code is available when implementing the function.
- For embedded methods, codeView can be strengthened, and RD tries to separate them into independent methods during development.
- For whether there is reusable code, RD needs to be familiar with the requirements first, and then to understand the function, you can ask whether there is reusable code for the original function development rd, for model layer, tool layer and other methods, you can fully search check, these are generally easier to find.
The project logic is entangled seriously, the responsibility is complex, and the dependency within the layer is serious
- Component bloat: The number of Service components is almost the same as the number of domain entity objects. As a result, individual Service components become very bloat, with many apis and thousands of lines of code.
- Ambiguity of responsibility: Business logic often spans multiple domain entities and is not appropriate in any Service. Similarly, the implementation logic for a function cannot be identified in any Service.
When we say “business logic contained in code”, what exactly are we talking about? There is no standard in the industry, and CRUD, which is often talked about, is actually a lower-level data access logic.
In my opinion, the business logic in code refers to all the input and output rules, algorithms and behaviors displayed by the code, which can be divided into the following five categories:
-
Input validity check;
-
Business rule verification: typically, such as checking transaction record status, amount, time limit, permission, etc., usually including database or external interface query as reference;
-
Data persistence behavior: any form of data writing to a database, cache, file, log, etc.
-
External interface call behavior;
-
Output/return value ready.
Of course, a particular component instance may not include all five types of business logic, but there may be multiple types of business logic.
This might not sound too complicated, but in reality each of the five types of business logic usually contains one to more underlying implementation logic, such as CRUD data access logic or calls to third-party apis.
For example, to verify input validity, you need to check whether the corresponding record exists. Before an external interface is invoked, you need to query related records to obtain parameters required for interface invocation. After an interface is invoked, you need to update the status of related records based on the result.
Obviously, there are two levels of logic — High Level logic that corresponds closely to business requirements and Low Level implementation logic.
If the logic of the two levels is not distinguished and confused, code quality is immediately seriously compromised:
-
Poor readability: Two dimensions of complexity — business complexity and the technical complexity of the underlying implementation — are mixed together, with complexity 1+1>2 increasing dramatically, placing a great burden on others to read the code;
-
Poor maintainability: Maintainability usually refers to the cost of troubleshooting and solving problems. When two levels of logic are entangled, troubleshooting will become more difficult and troubleshooting will be more prone to errors.
-
Scalability is out of the question: scalability usually refers to the cost of adding a feature to a system. The higher the cost, the worse the scalability. Similar to troubleshooting and fixing problems, logic entanglement can obviously make it difficult to add new features and inadvertently break existing features.
Here is a brief introduction to the application of Template Method design mode, which can be summarized as follows:
-
The High Level logic is encapsulated ina final function of the abstract parent class AbsUpdateFromMQ, forming a template of business logic.
-
Final functions ensure that the logic can’t be tampered with by subclasses, either intentionally or unintentionally, so it must encapsulate what is relatively fixed in the business logic. For those mutable parts and temporarily uncertain parts, reserve extension points in the form of abstract protected function;
-
A subclass (an anonymous inner class) fills in the template like a “fill in the blank” to implement Low Level logic — implementing those protected function extension points; Because the extension point is abstract in the parent class, the compiler reminds the subclass programmer what to extend.
So how does it avoid the four limitations of the two schemes above:
-
When a Low Level needs to be modified or replaced, it simply extends a new subclass from the parent class, and the parent class does not know anything about it.
-
Function is not visible to the XyzService component of the outer layer, whether it is a parent class or a subclass. Public function is not visible to the XyzService component of the parent class, because only the instance object that holds the class can access the function.
-
No matter the parent class or the subclass, they are as the internal class of XyzService, will not add new Java class files will not add a lot of meaningless API (API is only meaningful in the project reuse or published for external use, only the unique caller API is not necessary);
-
The problem of runaway component dependencies is certainly gone.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = end = = = = = = = = = = = = = =
The above kind of solution, in fact, the simple point is to separate the function as far as possible, the method is not too big, so that can ensure the code reuse, the method is more clear.
Ambiguity of responsibility: Business logic often spans multiple domain entities and is not appropriate in any Service. Similarly, the implementation logic for a function cannot be identified in any Service.
My solution is as follows:
Such as an Internet company (tencent), and a real estate enterprise (wanda) wants to enter the field of electricity, but this has certain intersection areas and their original but also has a bigger difference set (a social, but there was Internet gene, a real estate, channel genes) fusion can very good cooperation, if you can that how fusion, In fact, it is a better choice to deal with it independently. Therefore, Feifan was established. This new field can well integrate the two fields while ensuring the independence of their respective fields.
How do you do that
With separate domains, there are inevitably problems with class naming and code reuse, as well as calls between domains. Read on:
For naming problems:
Most of the current design ideas are to put the business logic in the service layer, such as the userService class in the User domain, product under the productService, You can also have the UserService class and the ProductService class in the UserProduct domain (never mind the same class name)
If you want to see what products are available under user, you can go directly to productService under userProduct.
For the inter-domain invocation problem:
From the naming logic and domain design above, it can be seen that there will be intersection between domains, and there is the possibility of reuse. For example, ProductService in userProduct field is dependent on ProductService in Product field.
In practical engineering implementation, small projects can be directly dependent on the introduction of the corresponding service. A better way to handle this is to add a new class to the domain to handle dependencies between the domains for later maintenance and replacement.
(Key point: business logic must be split and methods separated into small functions to ensure code quality and improve subsequent code reuse)
Similarly, the service layer can adopt the above design mode, and the Controll layer can also adopt the above design mode.