简体   繁体   中英

SOLID stored procedures and functions

I want to start using stored procedures more in an application I am working with. The application searches a dozen or so databases. The application stores information in its own database.

I was thinking about offsetting business logic to stored procedures in specific databases. Therefore if the logic is generic to all external databases then hold it in the application (.NET). If the logic is specific to a database then create a stored procedure.

I am unsure how SOLID works with stored procedures and functions as there are no interfaces or abstractions. The following post seems to suggest that you should try to consolidate queries: http://ledgersmbdev.blogspot.co.uk/2013/02/building-solid-databases-interface.html . For example, if a stored procedure has four SQL statements, then why not try to combine them into one SQL statement? Is this what the post is saying? Is this a SOLID approach?

I was thinking about offsetting business logic to stored procedures in specific databases. Therefore if the logic is generic to all external databases then hold it in the application (.NET). If the logic is specific to a database then create a stored procedure.

This statement causes great concern in my mind. When you say if the logic is specific to a database, to which I presume you mean one of the twelve aforementioned, this is a design flaw. Databases are just that, information stores, they should not require any "special" logic to access them outside of whatever view structure is exposed. Further, if the data needs to be manipulated in a non-set manner than you need to put it inside your application. That is don't make your perform calculations when they can be offset to your application.

When you design your application you must ensure that you are not morphing your database to ignore the relational model. In my experience this is one of the best ways to make your application unmanageable and slow. To clarify, business logic should not live in the database, this makes it hard for others to use your data. The typical argument against this is, "I am the only one using the data", to which I say that is an awful design reason.

Moving on, you should try to determine what actually works in the relational (set) model and build an application that can query that data. Instead of building a database to fit your application. That being said, SOLID DOES NOT WORK WITH RELATIONAL MODELS AS THEY ARE NOT OBJECT ORIENTED
WIKI

In computer programming, SOLID (Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion) is a mnemonic acronym introduced by Michael Feathers for the "first five principles" identified by Robert C. Martin 1 [2] in the early 2000s[3] that stands for five basic principles of object-oriented programming and design.

UPDATE FROM COMMENTS

The application links information from different systems and decides when a group of records can be deleted (these are generic rules that apply to all systems). There are then database specific rules that must be applied before deleting. I was thinking about offsetting the local business rules to stored procedures.

Looking at this comment, I don't fully understand what database specific rules would be in place outside of auditing / soft deletes. I would agree that database specific rules can be set to administrative stored procedures in the database, where you have to draw the line is when you get to issues such as the following:

My application queries against historic data unless it is older than 6 months then it must be retrieved from offline storage.

The option you have for removing this data after 6 months , is to allow the application to purge it via some business logic OR to create a scheduled task to do this cleanup as it is a normal database operation to remove tuples.

My argument here would be to place this in the database and to disallow your application from invoking these procedures. In fact your application shouldn't even know a database exists, in a correctly abstracted application. So if this is the example you are proposing then my solution would be as follows:

1) Create stored procedures in the database that only a maintenance based user can invoke,   NOT THE APPLICATION  
2) Create a database scheduled task to run these based on your data needs.  

I agree with the notion of not putting business logic into SQL as well the concept of SQL being a set based language.

Think of SOLID as an OO specific implementation of some really good programming practices and apply them to any code you write, including SQL. Good table schema design will incorporate SOLID ideas.

I have spent too much of my life debugging large complex stored procedures so if you can avoid it, please do. If you must write stored procedure code then SOLID can help you.

For example I have a large Stored Procedure which queries against a few dozen tables and it returns a data set for use by the printer to send 'welcome letters' . That is a classic example of a business process disguised as a 'report' and written in SQL.

Looking at this operation from a SOLID lens:

It should have a Single responsibility and a Single reason to change.

The code both determines who gets a letter as well as what data goes into that letter. If either aspect of the process changes you have to update and retest the entire system. A solid principle would be to have a function that determines who gets a letter and what is in it.

This could be as simple as having a single query which will return a list of customers. Feed that as an array of IDs or TVP into another stored procedure to collect data.

Open / Closed

In most SP (Stored Procedure) code the entire procedure is either hard coded or 'data driven'. Data driven is a usually a SQL effort against this principle. The SQL flow is modified by a CASE pivoting off a field value or SQL in text fields that gets EVAL'd.

A SOLID approach to my problem can be seen in the 'master' SP that we just created Now I have a SP that calls GET_CUSTOMERS and another that calls GET_DATA . This master basically controls the workflow and that workflow should not change. The master is closed for modification.

If we then need to enhance the system to send emails, and emails have different data fields then I can swap out the call to GET_DATA with a call to GET_DATA_FOR_EMAIL . Open for modification .

The master has a single responsibility. If the workflow changes, then this master changes, as per rule #1.

Liskov substitution principle

This is very OO, but to make a point let's say that the GET_CUSTOMERS SP can been seen as a replaceable object here. I should be able a different lookup, say GET_CUSTOMERS_WHO_NEED_DIFFERENT_LETTER and be able to reuse my code. Or maybe GET_CUSTOMERS_FOR_INTERNAL_TEST ? If all the components of your system as one –off and not reusable then the system will be harder to maintain and understand. This one may be a stretch. Think less about OO object inheritance and more about functions with similar arguments that can be reused.

Dependency inversion principle

This is the biggest FAIL I've seen. Don't write the code around selects and where clauses. Where my system started with a *SELECT * FROM table, table, table where customers.letter-sent=false* (actually over 1k lines) I will hopefully end with Find all customers and send them a letter . Start with the abstractions and code to where the highest level controls the workflow in the SP.

The idea is that the abstraction is 'find customers' and that returns a list of customers. I should be able to swap that logic for something else in the code. A violation of this which I often see is to have a 'find customers' that writes to a table joined on the customers table, and then another SP that reads that table. In that case your abstraction leaked into the implementation and you went from reusable code to a system that sends letters for a specific table.

SQL is very powerful and it is way too easy to combine steps that should be separate in the abstraction to be comingled in the implementation.

Stored Procedures are a good tool that helps team exchange readability for performance and brings convenience at the expense of scalability. It is still code and stealing patterns from other languages will only help if your overall design.

Very often you don't need a design pattern if your needs a simple. If your code is more then 50 lines or so try to move it out to an app layer and / or think about a solid re-factoring.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM