Repositories Tell Don’t Ask
This post was created by Codeweavers Software Developer Tom Horan and the original source of this post is Tom's website. It can be found here
I believe tell don’t ask is one of the most overlooked components of Object Orientated programming. I will be honest, it takes more thought and time initially but more times than not it will result in a better and more habitable design.
Repositories by their very nature are prime violators of tell don’t ask. A common pattern is to ask a repository for a record and then perform some logic based on the record that was returned.
In this example we get a user from a repository based on username and/or email, if the user already exists we return an error, if not we register a new user.This leads to records being leaked all over the place and logic being duplicated and spread through the application.
One alternative that I like to use is instead of returning records or DTO’s from my repository I will return a business object and then call a method on the business object that performs the logic.
This example encapsulates the knowledge of whether or not to create a user in the user business object returned from the repository. If the repository cannot find a user it will return a blank or null user business object which when Register is called on it, will perform the appropriate action, in this case return an error.
As you can see I am telling the business object to do something, it knows what state it is in and therefore knows how to perform the required logic. However I am still asking the repository for the business object， now this isn’t the worse thing in the world, it’s better than returning a record, but I wanted to see if I could remove asking the repository for something and instead tell the repository to do something. Another thing to be cautious about when using business objects in this manner is that they can quite quickly get rather large， every time you need the business object to do something different you have to create a new method on it.
This is where C#’s Functions and Actions come in handy. The first step was to allow the search criteria to be passed into the repository so I could have one generic repository. Then the repository needed to know what to do if it found a match and what to do if it didn't. I ended up with the following code.
The problem was as you can probably tell using the repository in this manner doesn't read very cleanly. Looking at the example above it’s not clear exactly what is going on. To solve this I created a static Query class which encapsulated the repository call and made the code more readable.
As always there are many other ways this could be achieved, this is just the solution I came up with at the time.
I hope you found this useful and next time you are writing a repository and returning a DTO or worse a record, stop and think, is there anyway you could encapsulate the logic into an object and return that from the repository instead or even better find a way to not return anything from the repository, as always have fun and don’t be afraid to try something different.