📜 ⬆️ ⬇️

The principle of separation of responsibility and ORM

I would like to discuss the principle of separation of responsibility (Separation of Concerns, SoC) in the context of ORM, and also to see why this principle is so important. We will also consider examples of violations of the boundaries of responsibility between the domain logic and the logic of data preservation.

Principle of sharing responsibility


In each application we deal with several concepts (concerns). At least three of them are usually well defined: UI, business logic and database. The principle of separation of responsibility is closely related to the Single Responsibility Principle (SRP). You can think of SoC as SRP applied not to a single class, but to the entire application. In most cases, these two principles can be used interchangeably.

In the case of ORM, the principle of SoC refers to the separation of the logic of the subject (domain) domain and the logic of storing data in the database. We can argue that the application code has a good degree of separation of responsibilities if the domain classes in it do not know how they are stored in the database. Of course, it is not always possible to achieve a complete separation of these two areas of the application. Sometimes the performance requirements are such that you have to violate these boundaries. But in any case, it is always worth striving for as complete a division of responsibility as possible.

And of course, we cannot just separate the domain logic of the application from the logic of storing data in the database, we need something that connects them together. This is where ORM helps us. The ORM mediates between the domain model code and the database. In most cases, the ORM is able to do this in such a way that neither the domain code, nor the database is aware of the existence of each other.
')
image


Why is SoC important?


There is a lot of information on how to maintain a good degree of separation of responsibilities. But why is this important?

image


While maintaining different responsibilities in a single class, we have to code-hold their consistency simultaneously with each operation within this class. This very quickly leads to a combinatorial explosion. Moreover, the complexity of the application is growing much faster than most developers think. Each additional responsibility increases the class complexity by an order of magnitude.

To cope with all this complexity, we need to share these responsibilities:

image


SoC is not just a matter of good or beautiful code. The principle of SoC is vital to maintaining an acceptable development speed. Moreover, it is important for the success of your project.

A person can hold no more than nine objects at a time in short-term memory. An application without a clear division of responsibilities very quickly overwhelms the developer’s short-term memory due to the sheer number of combinations in which different undivided concepts can interact with each other.

Splitting these concepts into highly related parts allows you to “divide and conquer” over the application you are developing. It is much easier to manage the complexity of a small, isolated component that is loosely coupled with other components of the application.

When the logic of saving data in the database penetrates the domain logic


Let's look at examples in which the logic of data preservation penetrates into the lock domain.

Example 1: Working with the persistent status of an object in a domain model class.

public void DoWork(Customer customer, MyContext context) { if (context.Entry(customer).State == EntityState.Modified) { // Do something } } 

The current persistent state of the object (that is, whether it already exists in the database or not) has nothing to do with the logic of the domain model. Ideally, domain objects should operate only with data that directly relates to the business logic of the application.

Example 2: Working with IDs

 public void DoWork(Customer customer1, Customer customer2) { if (customer1.Id > 0) { // Do something } if (customer1.Id == customer2.Id) { // Do something } } 

Working with identifiers in domain classes is perhaps the most common type of mixing different types of application responsibilities. Identifiers are part of the implementation of how your objects are stored in the database. As a rule, they are used to compare objects with each other. If you also use them for this purpose, a much better solution would be to override the comparison operators (equality members) in the base class of the domain object and write 'customer1 == customer2' instead of 'customer1.Id == customer2.Id'.

Example 3: The separation of properties of a domain class by persistent attribute

 public class Customer { public int Number { get; set; } public string Name { get; set; } //    ,       public string Message { get; set; } } 

If you tend to write such code, then you should stop and think about the domain model. Such an approach may indicate that you have included elements in the domain model that are not related to it.

When domain logic enters the database


Example 1: Cascade Delete

Configuring the database for cascade deletion is one of the examples of the infiltration of domain logic into the data storage logic. Ideally, the database itself should not contain information on when the deletion of data should work. Such knowledge is a domain concern. Your C # / Java / etc code should be the only place to store this logic.

Example 2: Stored Procedures

Using stored procedures that modify data in a database is another example. Do not allow domain logic to penetrate into the database, store code that changes the state of the data in your domain model.

Here it is necessary to make two comments. First, in most cases, there is nothing wrong with having stored procedures that do not change the data in the database (read-only stored procedures). Placing the code that leads to side effects (side effects) in the domain model and the code without side effects in the stored procedure is perfectly correlated with the principle of CQRS .

Secondly, there are cases when it is impossible to avoid using SQL. For example, if you need to delete a group of objects on some basis, the SQL DELETE statement will do this work much faster. In such cases, the use of SQL, which modifies the data in the database, is justified, but it is necessary to keep all such exceptions under strict control and not let them grow.

Example 3: Default Values

The default values ​​in database tables are another example of domain logic that has penetrated the database. The values ​​of the properties that the domain entity has by default should be defined in the code, and not relegated to the database.

Think how difficult it is to collect such knowledge piece by piece from different places of the application. It is much easier to store them in a single place.

Conclusion


Most of these "leaks" arise because people think about their application not in terms of the subject area, but in terms of data. Many developers view the system they are developing in this way. For them, classes are only a repository for data that they transfer from the database to the UI, and ORM is just a utility that helps not to manually copy data from the results of executing SQL queries into these objects. It is often difficult to make a shift in the paradigm of thinking. But after it is done, people usually open up a whole world of expressive domain models that allow you to develop applications much faster, especially on large projects.

Of course, it is not always possible to achieve the desired level of separation of responsibilities in the application code. But in most cases, nothing prevents to do this. Most projects fail not because they are not able to fulfill any of the technical requirements. Most fail because they are buried under a mass of erratic code that prevents developers from changing anything in it. Every change in this code leads to a cascade of bugs and unexpected side effects throughout the application.

SoC is a principle that allows you to avoid such an outcome.

Link to original article: Separation of Concerns in ORM

Source: https://habr.com/ru/post/263027/


All Articles