Thanks to heaven for it was raining on Saturday, and I read it (and you say thank you for translating). On Sunday, however, the sun was shining and text formatting was postponed.
Special thanks to the author for allowing a separate publication.
An extremely interesting article about what business logic is and where to live. The article, by the way, is already three years old. And I often meet systems where the code is not separated from the data. May lead to real holivaru.Where is our business logic, son?
Introduction
Over the years of development, we have moved from the desktop to the client-server architecture, then to the 3-tier design, to the n-tier, to service oriented. During this process, many things changed, but many habits remained. Often, resistance to change comes from habits. However, in many cases it is procedural. This article describes what we are doing wrong and possible solutions.
')
About the article
What I will describe here is one of the methods for constructing n-tier systems in terms of design and architecture. This article does not focus on code. There are many methods for constructing n-tier systems, this is only one of them. If you are building a system, I hope you will find a good tip, technique or pattern for using this approach.
While this article may offer several starting points from “standard methods,” everything in this article is based on Microsoft Patterns and Methods and is described in
Designing Data Tier Components and Passing Data through Tiers and other documents.
Even if you do not dare to apply all the methodologies proposed here, you should use at least some of them.
purpose
Ask any developer where business logic should be, and get the answer: “Of course, in the business layer.”
Ask the same developer where the business logic is in their organization, and again hear: “Of course, in the business layer.”
You should have no doubts about where business logic should be - in the business layer. Not part of the business of logic - all business logic must be in the business layer. After reading this article, many developers will understand that what they thought was true about their systems is not.
Terms
These terms are often used together, but in this article I will use them as described here.
Link (tier)
When I use the word link, I mean a physical link consisting of a physical server or a group of servers that perform the same function and are grouped only to increase capacity.
Layer
When I use the word layer, I mean a system segment that is limited by its own process or module. Multiple layers can be contained in one link, but any of them should be able to be easily transferred to another link.
Problem development
Desktop
On desktop applications, business logic is contained on one link with all other layers. Because there is no need to separate the layers, they are often mixed and do not have clear boundaries.

Client server
In the client-server application, there are two links, which leads to the creation of at least two layers. At the initial stage, the server was considered only as a remote database, and the division was as in the figure - the application on the client and the data on the server. Usually all business logic was on the client, mixed with other layers, such as the user interface.

It quickly became clear that you can reduce the load on the network and centralize logic to reduce fixed deployment costs by transferring most of the business logic to the server. Architecturally, the server was a well-prepared place in the client-server system, but the database as a platform offered few opportunities. The databases were designed for storage and distribution, and their architecture did not allow for expansion in the direction of business logic. Database stored languages ​​have been developed for basic data transformations to support what SQL was lacking. The stored procedure languages ​​have developed a business logic for quick execution and not for handling complex tasks.
But this was less of two evils, and part of the business of logic moved into stored procedures. In fact, I am willing to bet that business logic has been narrowed and driven into the framework of stored procedures, exclusively from a pragmatic point of view. In a two-part world, this was not perfect, but still much better.

3-link
When the problem of the client-server architecture became apparent, the popularity of the 3-tier approach increased. The greatest and most difficult problem of that time was the number of connections. Now many databases can handle thousands of one-time connections; in the nineties, most databases dropped somewhere in 500 connections. Servers are often licensed by the number of client connections. This all led to the fact that it was necessary to reduce the number of connections to the database.
Connection pooling has become popular; however, to implement a connection pool in a system with many individual clients, it is necessary to implement a third link between the client and the server. The middle link became known as the “middle link”. In most cases, the middle link existed only for managing the connection pool, but in some cases the business logic began to move to the middle link because the development languages ​​(C ++, VB, Delphi, Java) were much better suited for implementing business logic than the stored procedure languages. It soon became apparent that the middle link was the best place for business logic.
Also, the middle link provided the ability to connect customers with low speeds, because Direct connection to the database usually requires a wide channel and low latency.
What is business logic?
Before I continue, let's clearly define what business logic is. Making presentations at conferences and inside the company, I began to fear that not everyone agrees with what business logic is, and, quite often, they do not even fully understand what it is and what it is not.
The database server is the storage level. Databases are designed to store, retrieve and update data with the highest possible efficiency. The functionality is often a SUPUM (Create, Delete, Receive, Update). Some databases are supo and are, but the conversation is not about that.
The databases are designed to serve these operations very quickly. They are not designed to format phone numbers, calculate optimal usage and peak loads, determine geographic location and routes of loads, and so on. Although, I have seen all this and many more complex tasks implemented only with the help or in large part in stored procedures.
Buyer delete
And all this applies not only to complex things. Let's imagine a simple task and one that is often not even attributed to business logic. Task - Remove Buyer. In almost all systems that I have seen, the removal of the buyer is handled solely by the stored procedure. However, in the removal of the buyer, quite a few decisions must be made at the level of business logic. Is it possible to remove the buyer? What processes should be started before and after? What precautions should be observed? From which tables should records be deleted or updated afterwards?
The database should not be concerned with what a customer is; it should only care about the elements used to store the customer. The database should not be able to figure out which tables the customer should store, and it should work with the tables without paying attention to the customer. The task of the database is to store rows in tables that describe the customer. In addition to basic constraints such as cascade integrity, data types, indexes, and null values, the database should not have functional knowledge about what the buyer is in the business layer.
Stored procedures, if any, must operate with only one table; an exception is a procedure requesting a sample of several tables to output data. In this case, stored procedures work as views. Views and stored procedures should be used to consolidate values, but only for faster and more efficient work with data in the business layer.
But even in companies that are proud of the latest achievements in development and technology, and in those that shout at the mouth about their entire business logic in the business layer, a brief database analysis quickly reveals: remove a buyer, add a buyer, block a buyer, freeze a buyer etc. etc. And not only with the buyer, but also with many other business logic objects.
I have often seen stored procedures like this:
sp_DeleteCustomer(x)
Select row in customer table, is Locked field
If true then throw error
Sum total of customer billing table
If balance > 0 then throw error
Delete rows in customer billing table (A detail table)
if Customer table Created field older than one year then
Insert row in survey table
Delete row in customer table
Regularly part of the business logic moves off to the business layer.
Business Layer (C#, etc)
Select row in customer table, is Locked field
If true then throw error.
Sum total of customer billing table
If balance > 0 then throw error.
if Customer table Created field older than one year then
Insert row in survey table
Call sp_DeleteCustomer
sp_DeleteCustomer(x)
Delete rows in customer billing table (A detail table)
Delete row in customer table
In this case, part of the business logic has been moved, but not all. Some tables are processed in the business logic layer. The database should not have any idea what tables form the buyer in the business layer. For all three operations, the business layer must issue a SQL command or call three separate stored procedures to implement the functionality in the above sp_DeleteCustomer.
By transferring all business logic to the business layer, we get:
Business Layer (C#, etc)
Select row in customer table, is Locked field
If true then throw error.
Sum total of customer billing table
If balance > 0 then throw error.
if Customer table Created field older than one year then
Insert row in survey table
Call sp_DeleteCustomer
Delete rows in customer billing table (A detail table)
Delete row in customer table
Deleting rows can use a stored procedure if they are from the same table. However, in modern databases using query caching, this is not a significant performance improvement. In addition, the SQL generated by such systems is very simple, because it works with a single table, and therefore requires almost no optimization. In fact, the database does not become very good from too many stored procedures loaded, and simple SQL commands do not work on them like that.
Transferring even the modification of the tables in the business layer, we will get the following benefits:
- Database transfer can be carried out with less effort, because All these stored procedures do not need to be debugged for each DBMS.
- Modification is easier, because all logic is contained in one layer, not in two.
- Debugging is simpler - the logic is not spread over two layers.
- Other logic will not be able to slip into the stored procedure just because "that way is easier."
Since this method requires three successful calls to the database instead of one, your business logic node must be connected to the database on a separate high-speed segment, such as gigabit. Sending 300 bytes instead of 100 bytes will become unprincipled. Most databases support batch transfer of SQL queries, and all three queries can be sent in one packet, reducing the load on the network. To issue such requests, you should use the data access layer, and not include the requests directly in the code.
Some database administrators and even developers may not accept this level of integration and insist on implementing such batch updates in stored procedures. This is a choice that you must make, and it is very dependent on your database and your priorities. Because almost all modern databases use query caching mechanisms, performance gain in most cases is minimal, and there are clear technological reasons for not loading logic with stored procedures. If you choose to leave such batch updates in stored procedures, you must be very careful to prevent other business logic from slipping into stored procedures, and limit your stored procedures to SOUP operations, without any conditional operations or other business logic.
Formatting
Let's look at another example that I discovered and sowing the seeds of war among developers - is this business logic or not. I’ll tell you why I think this is business logic, not user interface or storage. This example does not apply to easily implemented formatting. An example that I will use is phone numbers.
Each country has its own format for displaying phone numbers in a pleasing manner. In some countries they are even more than one. Below are a few examples:
Cyprus:
+357 (25) 66 00 34
+357 (25) 660 034
+357 25 660 034
+357 2566 0034
Germany:
+49 211 123456
+49 211 1234-0
North America (USA, Canada)
+1 (423) 235-2423
+ 1-423-235-2423
Russia:
+7 (812) 438-46-02
+7 (812) 438-4602
In Germany there is even a special official standard for formatting - DIN 5008.
Of course, the country code is discarded when used locally. But let's assume that you have an international system and you need to store and display the country code. For each country, we will choose one display format.
We agree to format the phones as follows:
- Data comes in a variety of formats.
- Each country has its own unique way to display phones.
- The formats of some countries are not simple and vary depending on the first numbers.
- The first few digits (usually the country and region code) do not always have a fixed length. For example, in Russia, 812 is the code of the city of St. Petersburg, 495 is Moscow, but some regions have 4 characters (3952). This leads to a change in both the overall length and the format, depending on the region code.
- With the release of new laws, the emergence of new operators, the integration of the European Union, the renewal of telephone systems and many more, the formats and lengths of telephones change quite often on a global scale. In recent times, Cyprus has changed its country code twice: once when updating the system, a second time because of the increased number of mobile operators. With hundreds of countries around the world, we should expect changes on a regular basis.
Usually the following is done, all non-numeric characters are removed and the number becomes similar to:
Phone: 35725660034
Sometimes a country code is separated and the number becomes:
PhoneCountry: 357
PhoneLocal: 25660034
It seems simple, but this is another task for business logic. Not all countries have a code of the same length. Country codes can be from 1 to 3 characters.
Often, input processing (if the country code is separated) and the display logic are implemented on the client, since The client is written in a traditional language that is well suited for this. The problem is that the client needs a huge amount of data to determine the length of the country codes, and the client will need to be updated every time the display format changes.
Sometimes formatting is done in a stored procedure. The problem with this approach is that the languages ​​of the stored procedures are not suitable for this type of logic, and it often leads to bugs and brakes in working with real logic.
More often phone numbers are stored twice. Once pure for good indexing and searching, and the second in formatted for display. In addition to the problems described above, we get the problems of redundant entries and updates.
In especially sophisticated extreme sports, which are often ridiculous, the telephone number is stored in the format in which it was received. The problem is obvious: phones can not be quickly found, indexed or sorted.
The important thing is that although this is formatting, it does not belong to the user interface, and an attempt at total centralization can shoot the database. This is definitely a business logic. The implementation of formatting in the business layer will not allow duplication of data and will be written in the development language, and not hammered into the data processing language.
Exceptions
Some batch updates are performed many times faster when implemented using stored procedures. In most cases, simple SQL can be avoided, but some types of batch updates require cycles and, when implemented, create thousands of SQL commands in the business layer. In such rare cases, a stored procedure should be used, even if it needs to implement business logic. It is necessary to pay special attention to the fact that it was implemented only the necessary minimum.
I will return in the article to this problem.
Today's systems
Client server
In client-server applications, business logic is usually available on both the client and the server.

The real ratio will vary from application to company, the previous example describes client-server applications well. Most business logic was implemented in stored procedures and views in an attempt to centralize business logic. However, many business rules cannot be implemented simply by SQL or stored procedures, or they can be faster executed on the client, since they are based on the user interface. Due to these opposite factors, business logic is distributed between the client and the server.
N-ring
For many reasons, which I will describe later in a separate article, when building n-tier systems, the situation only gets worse in terms of consolidating business logic. Instead of consolidation, business logic becomes even more fragmented.
Of course, each system has differences in how business logic is distributed among the layers, but there is one thing common to all. Business logic is now divided into three layers instead of two. Next, I will present some typical scenarios.
Scenario 1
Typical distribution of business logic over an n-tier system:

In such cases, the business layer does not contain business rules. This is not a real business layer, but only an XML (or other streaming format) formatter and a database dataset adapter. Although some advantages such as: connection pooling and database isolation can be achieved, this is not a real layer of business logic. It is rather a foreign physical layer without a layer of logic.
Scenario 2
Another typical scenario:

Usually, some business rules of an application go into the business layer, but what was in the database remains in it for the most part.
When reusing a business layer in such designs, business rules should be repeated in the client application. This negates the main purpose of the implementation of the business layer.

Also, client applications have the opportunity not to comply with business rules without implementing them or simply ignoring them. With the presence of a real business layer, this is impossible.
Consolidation
Instead of all of the above, the business layer should contain all the business rules.

Such development has the following advantages:
- All business logic is in one place and can be easily checked, debugged and changed.
- Normal development language can be used to implement business rules. Such languages ​​are more flexible and more suitable for business rules than SQL and stored procedures.
- The database becomes a storage layer and can be engaged in efficiently obtaining and storing data without restrictions related to the business logic or presentation layer.
The above scenario is the goal. However, some duplication, especially for data verification, should also be on the client. These rules must be supported by the business layer. In addition, on some systems, individual high-capacity operations, such as batch updates, can lead to exceptions and should be placed in the database. Because a more realistic approach is presented below. Please note that all business logic must be implemented in the business layer, and those minimal sets that are present in other layers are simply duplicates solely to improve performance or disable certain components of the user interface.

Moving to the central hub
Slippery slope
When switching to the central node, there is always the temptation to "implement this part in a stored procedure." Then "that" and "this one." And soon you will find yourself in the same situation as you were, without significant changes.
Stored procedures should be used to execute SQL and retrieve data sets in databases that optimize stored procedures better than views. But stored procedures should not be used for anything other than combining and issuing data. When updating data, it should exactly and only update, but not interpret the data in any way.
There are tasks where, to improve performance, some components must be placed in a stored procedure. But such tasks are actually quite rare and they should be the exception, not the rule. Each exception must be verified and approved, and not simply implemented at the behest of the developer or database administrator.
Cheaper
It sounds somewhat strange that buying iron can make it cheaper. But with the implementation of middle-level servers, practically no additional software, except for the OS, is required. And the cost of increasing the capacity of the database server is significant for the following reasons:
- Database servers are usually higher-grade than middle-level servers and cost more.
- Databases are often licensed for the processor and adding a processor is an expensive procedure in terms of licenses. Licensing fees can range from 5,000 to 40,000 dollars per processor.
When transferring logic to the middle tier, you can significantly reduce the load on the database and prevent its capacity from increasing prematurely.
Simpler
In addition to cost, mid-level updating is usually easier than updating a database.
Databases have an inherent limit on how much they can be increased by simply adding iron. At some point, you need to start using other technologies like division, clustering, replication, and so on. But none of these technologies is simple, and all require substantial investments in iron, migration, and strongly affect existing systems.
Increasing the mid-level server is much easier. As soon as the load balancing mechanism is launched, it all comes down to the task of adding a new server.
Topology
Let's look at the statements I just made using the following diagram. The fill in segments shows the direction or importance of their name in relation to the links in the diagram. The unit price increases when we move from the client, to the middle link, to the database. I use the word unit to indicate a processor or server, depending on the configuration.
(top to bottom: unit price, average bandwidth, deployment complexity, quantity)

If the same data is given in relative values, they can be easily compared:

I did not give the numbers on the charts because they are very dependent on the network configuration, processor power and other factors that are unique to each organization. Each function uses its own units of measurement. I presented only the general relationship of measurements. It shows well that the middle link has capacity for growth and is much cheaper than a database.
Grow middle
If most business logic is implemented in a database, you will need a more powerful database.

When transferring logic to the middle tier, you can seriously reduce the load on the database. The figures presented here are for demonstration purposes only and will vary from system to system, but they can help you get the idea. Although the following diagram and more hardware, the total cost of the system will be less, and it will be easier to deploy. It is much cheaper and easier to build a middle link.

Bottleneck
Let's look again at one of the previous graphs:

What is the only bottleneck in the system? Which of the links has a pronounced limit of growth? This is definitely a database. It all comes down to the database.

Therefore, by moving the calculations to the middle link, we can move away from the boundaries of the data layer.
Difficulties
There are several difficulties to go to the middle link, and not all of them consist in the need to program differently.
Habits
There is a saying: “It’s hard to get rid of old habits.” This applies to the team. In a team, you need to convince not only yourself, but most of the team.
Procedures
Many companies have well-established security policies that enforce security in the database, and using stored procedures as representations does not provide sufficient control. Changing corporate security policies to go to the n-tier world can be very difficult, if not impossible.
.Net , Microsoft, , , .
. , -, . , , , . , .
, – ! , .- , . . – , . , .
, n- , . , - . , - .
, 10 20 . , . . .
, . , , , .
Tools
, . , , .
Solutions
Architecture
, . , . , . - , .
. , : , . , , , .
. , . SQL, , . .
, , , . , , , , , .
. , .
What else to read
, . , , .
, , «». , , . , . , , . , , .
, - . , 2002 Microsoft. , .
,
Designing data tier components and passing data through tiers.Conclusion
. . , . , , , .
, . , - .
UPD: maovrn « ».
UPD1::
1. .
2. , .1.
Chad Z. Hower aka Kudzu3. , —
. , , , .
4. — .