Repositories: Tell Don’t Ask

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.

5 thoughts on “Repositories: Tell Don’t Ask

  1. DTO’s are meant to transfer data from one layer to the next. Instead of putting logic in the DTO you should create a logic layer and expose that to your front end. Typically this is done through a service so that every application depending on that logic is updated simultaneously. If you put the logic in the DTO and deploy it in multiple applications you could have misaligned logic in each application unless you go through the hassle of redeploying all of them. This is why microservices has become popular.

    1. I like the idea of having a service for your logic and this is separate from your data retrieval, having multiple application or systems able to use this logic independently of where they get their data from.

      Microservices is something I am quite interesting in and hoping to do an article on one day.

      Thanks for the comment.

  2. Interesting example, the use case of user registration.

    However I don’t agree with the idea that repositories should employ a “tell, don’t ask” principle though. Most of the time you want something from a repository so you actually do want to “ask” aka query it.

    There is a bit of noise in the community about anti-repository solutions and avoiding them all together. I’m generally sold on this in most cases.

    The refactoring to use a generic repository “GetBy” is something that has burned many in the past too. You’ll get many benefits by not doing this oddly enough, and also sidestep the TOCTOU issue the last example has.

    The idea that “tell, don’t ask” should be used more is great though. You’ll get a lot of bang for you buck just by implementing your second solution and keeping it simple.

    1. Many thanks for the feedback I really appreciate it.

      I find CQRS interesting, I’ve never really developed strictly with that in mind, but I may try as it does make a lot of sense. The difficulty I find is is knowing at what level to apply it, in your article you applied it at an aggregate/business object level, my second example would probably be how I would implement CQRS in this situation, as the command to register a user shouldn’t know how to get a user.

      The idea of not using a generic repository is interesting, it’s one of those things that when you first think about it, it seems like the next logical step but as you presented in your article it can actually cause a few issues.

Leave a Reply

Your email address will not be published. Required fields are marked *