Often we deal with database. Data is flying back and forth between application and data store. Following the modern influences, we wish to have tested everything that might break some day. When you get enough experience and develop the sense of well code design, this task will become easy and straightforward, but unfortunately I can see very often bad samples of code separation and awful design. So what it is all about?
I don’t want to give you any samples of code just because it would tie us to some particular language. Here I intend to talk generally, to talk about good design decisions and testability.
Let’s look closely at what we have today. There are several levels of “badness” for code working with database today:
- You have direct database access calls in your business methods
- You have database access layer but you still using it in the mix with business logic
- You have your database access layer and your business logic separated, having only very thin layer to connect these two and route calls between them.
The first two are easy to understand, but what a hell is that third one? First we should ask ourselves, what we are planning to test? When you write an application working with database, you should test database layer in only two cases:
- To test how complex queries and updates are executed
- To measure scalability of your solution on test data
In all other cases it’s better to isolate yourself from database layer and forget about it. Concentrate on what you are going to test. If you code has mix of database access methods and business logic, you aren’t going to have a chance to test your business logic without real database or database layer substitution. Here’s the place where we should stop for a while and review available options. We have two major options (and that’s why this writing is called like it is). We can:
- Mimic database. You can substitute database access methods with own stubs returning a test data.
- Separate business methods.
Trying to mimic database looks much like if you were filling it with test data before running tests. You still need the database to test your logic, which is no fun – it eats resources, time and efforts, while you don’t need all that overhead at all. You still can do that, but if you aren’t patient enough, you’ll get tired soon (in fact, you will get tired of that sooner or later in any case), and that is going to be the end of your code testing practice.
In contrast to that, the separation of business methods can significantly reduce your efforts, required to build tests for the code. Usually, business processing follows the same common scheme: take this, take that, do some operations, make decision, put it there. If you were building design from business methods first, you could have your logic grouped in classes by domain. The methods of this classes could accept some parameters and return the results doing all necessary business decisions. They don’t know where the data comes from and, moreover, they don’t care about that. It simply does operations over the input in order to produce some output. Do you feel the power and ease of this design already? The benefits of this approach are obvious:
- You have a nice reusable library in narrow problem domain which has no connections to any specific data sources.
- This library is completely testable.
- As it separated from the rest of the code in your application, the development of it can be arranged for some member of the team, while others are busy with other modules.
The same approach is also applicable to the other areas of application – GUI, networking – everywhere, where you have some input and business logic applied.
I will leave you here with all these thoughts on hands. If you feel that I’m wrong, I would be more than happy to listen to your points. If this writing seem to be useful to you, also let me know please.
All good design decisions to you!