Most programmers have a common experience: when you’re working on a piece of complex code, you’re wondering who wrote this piece of code that looks like a lump of *, then you open the submission record and realize that you wrote it 3 months ago!

It looks so simple, but why is the software code so complicated? This is a question that all programmers are likely to ponder.

“Domain-driven design” claims to be a solution to software complexity. Its core idea is to design software from a business perspective and try to separate technical complexity from business complexity. But domain-driven design was invented 20 years ago, when software faced a different set of technical challenges and complexities than it does today, and some of the rules may not apply as well.

So why is software complicated? Some time ago, I read senior Zhang Yi’s Deconstruction Of Dome-Driven Design, in which there was a very detailed explanation of this aspect. I think he summed it up quite well. Here is a brief summary to share with you:

Simply put, software complexity is divided into two dimensions. In terms of the dimension of understanding, the larger and more chaotic the software, the more complex it is. In terms of predictive power, software may be too costly and complex to adapt well to future changes.

We can use some development concepts to correspond to these dimensions. For example, scale corresponds to lines of code and number of microservices. The structure corresponds to the hierarchical design of code and the invocation relationship of service; Overdesign refers to the design of software to be too general, resulting in complex code design and reduced readability; Poor design is a lot of dead code, resulting in business changes when the phenomenon of “inflexible”.

So what is the solution to domain-driven design? – “control scale” by breaking up fields and dividing and ruling; “Clear code structure” through sound architecture; High cohesion and low coupling to “cope with change” by abstracting appropriate domain models **

In the clear structure section, we should try to distinguish between “business complexity” and “technical complexity”. For example, an order business is concerned with the order verification, the calculation of the order amount, the submission of the order and the flow of the whole order follow-up status. Technically, the focus is to ensure that orders can be completed correctly, ensuring the correctness, consistency and stability of data, more specifically, such as how to prevent repeated submission, how to prevent oversold, how to deal with high concurrency, downstream service or database failure and a series of technical problems.

So let’s take an example of the “submit order” business to see what code is involved in the process, what is business code, what is technical code, and how it should be organized.

Let’s start with a simplified version of the submit order use case diagram, which could be much more complicated.

This use case diagram doesn’t look very complicated, with only six steps, which we’ll go through step by step.

Decomposition steps

1 Submit an order

The business doesn’t think too much about submitting an order. But technically, for the sake of security, we may need to verify the validity of some parameters, such as the id of the submitted goods cannot be an empty list, and the quantity of the submitted goods must be greater than 0.

This code is usually written in the outermost layer of our application and needs to be verified as soon as it comes in. Some languages, such as Java, may have a more elegant solution, such as using Validation annotations.

So ** “the parameter verification here is pure technical code” **.

Check and occupy inventory

In fact, from the business point of view, this step should only be called “check inventory”, that is, check whether there is inventory of this product, if the inventory is insufficient, should interrupt the process in time, prompting the user inventory shortage.

But for technical reasons, it may not just be a check. We also need to “tie up” inventory to prevent oversold. In seconds kill scenes, for example, a 100 inventory goods, but 10000 people at the same time, almost at the same time for everyone, if the inventory check the place limit, that is likely to have far more than 100 people were checking through, flow back, didn’t found until the user payment inventory shortage, give the user a bad experience, it is not allowed in our business.

I’ve combined this step together and called it “validate and hold inventory,” but in practice it could be two interfaces or one interface.

So ** “checksum here is business code, but occupancy is technical code” **. However, occupancy is also for better business experience, business data is correct, so some teams will consider occupancy as business code.

3 Query the commodity price

From a business perspective, all you really care about is the total price of the order. The calculation logic is simple: the sum of the price * quantity of each item in the order.

But technically to consider security, so this amount can not be transmitted from the front page, although at this time the front page has got the amount of each commodity data to show to the user.

To be on the safe side, the order system queries data from the merchandise system.

So the problem comes, if you can’t find a product (downstream hung or incorrect data), or this product has been off the shelf/has violated the rules?

At this point, if the product cannot be found because of downstream suspension or incorrect data, this is a technical problem, and the process should be terminated and the user should be reminded. If the product is removed/violated, it is a business matter. Of course, you should also terminate the process and prompt the user for the appropriate copy.

But no matter it is a technical problem or a business problem, if the price of goods cannot be queried normally here, for the sake of data consistency, “the inventory occupied in the second step should be rolled back”.

So the question is, what if rollback fails?

Here comes the question, “Is it worth making a plan for this” minimal probability scenario “? Like occupancy timeout release?

Because of the cost, I think most teams will not do this rigorously, in fact, it is impossible to solve this problem perfectly, after all, there may not be a perfect solution. If this happens, most people may choose to log an ERROR and then trigger an alarm to take a look manually.

4 calculate the total price of the order

Well, here we are in the realm of pure business. It may seem simple to calculate the total price of an order, just multiply it and add it up.

Then the business students tell you that there is no problem with the direct calculation of this section, but we may need to complete the subtraction activity later, which is classified. Up to 100 minus 10, 200 minus 25, 500 minus 80…

And there is a discount for members, 9.8 percent for VIP 1, 9.7 percent for VIP 2…

During the promotion period, we may also have a coupon event where users can use all kinds of coupons…

The most frightening thing is that the product students may not tell you these requirements when they raise requirements, but later put forward n requirements for you to change.

Well, that’s the business code. It can change at any time.

After the calculation, the order information should be saved to DB to generate a receipt. But the business doesn’t care about that. It’s technical code.

Confirm inventory deduction

In fact, in the business, putting in an order is a split-second thing, putting in an order, and then deducting inventory. But technically because it will be divided into the above steps, so there should be occupation, rollback, confirm deduction these steps. Also for data consistency.

So personally, “confirm deduction inventory” is more like a technical code.

6. Generate payment documents

I think this is business code. But there are also a number of technical problems, such as what to do about build failures, how to design idempotent, prevent double commits, and so on.

Domain code

Suppose we are a system divided by domain. So we have the order realm, the inventory realm, the goods realm, the payments realm. For e-mart, they are probably core areas in our system (maybe not payments, depending on company strategy).

In the business of user submission of orders, what the order field needs to do is to generate an “order” model, complete the calculation of the order, and generate the order documents. The calculation of completing the order is pure business logic and the most complex, and should be placed inside the domain model. But not always. When the rules are complex and volatile enough, we might use a “rule engine” **, and the order model’s job becomes “to calculate the amount by applying the rules read from the rule engine”.

In the inventory domain, the core domain code should be inventory occupied.

In the commodity domain, this step does not involve changing the state of the domain and is more about querying the commodity information. Under the CQRS (read/write separation) architecture advocated by domain-driven design, the commodity domain only provides a query interface, so the code associated with the domain model is not involved.

In the payment world, a “payment order” is also generated, where the logic is simpler.

Technical code

You can clearly see that it is almost impossible to successfully complete the “order submission” business operation by relying solely on domain code.

Inventory occupancy, for example, needs to be locked to ensure that it does not overoccupy. Not to mention rollback and validation. Validation of parameters and idempotent design, for example, are not part of the business code.

When we look at “event-based communication” between two domains, which is advocated by domain-driven design, it is not possible to use events entirely in this business. The order domain needs to call the inventory domain interface in real time to ensure strong consistency.

reflection

So let’s go back and say, can domain-driven design solve this problem? Can business code and technical code really be separated?

Clearly, in the realm of orders, a single order model can no longer hold all logic together. How about adding an order domain service? In theory, yes. The code organization looks like this:

public String submitOrder(SubmitOrderCommand command) { stockAdapter.checkAndOccupyStock(command.getCommodityInfos()); try { Commodities = commodityAdapter.Getcommodities(command.getCommodityInfos()); Order order = OrderFactory.generateOrder(command); order.computeTotalAmount(); orderRepository.save(order); stockAdapter.confirmOccupyn(command.getCommodityInfos()); } catch(Throwable t) {logger.Error(" Failed to submit order." , t) stockAdapter.rollbackOccupy(command.getCommodityInfos()); } paymentAdapter.submitPayment(order.getPaymentInfo()) }Copy the code

As you can see, the code above covers all steps from Steps 2 through 6 (step 1 is not included because parameter verification is usually done in the outer layer). But it was clear that we were still “inevitably mixing technical code with business code” **. For example, save the database, confirm occupancy, etc. But we did our best to move the technical code into the Adapter and Repository implementations and just call the interface at the domain level. Using domain services and domain objects is much cleaner than the traditional spaghetti of various service invocation codes.

Back to the problem of over-design and under-design. This is a test of how well a programmer understands and thinks about the business. If it is obvious that significant changes can be expected in the future (such as the changes in computing rules mentioned in the article), it really should be designed with more flexibility at the beginning of the design. If the future changes are not clear or certain, it is ok to meet the current business needs, but if the architecture is right and the code is clear, it is not that expensive to change. The idea here is to communicate as much as possible with domain experts (such as business people or product managers) so that you can better understand the future direction of your code.

And a support

My name is Yasin, a blogger who insists on original technology. My wechat official account is: Made a Program

All see here, if feel my article write also ok, might as well support once.

The first article will be sent to the public account, the best reading experience, welcome your attention.

Your every retweet, attention, like, comment is the biggest support for me!

There are learning resources, and frontline Internet companies within the push oh