When I say data access methods, I’m talking about the methods my UI is going to call whenever it needs to get data. When my Posts controller needs a list of BlogPosts to display, it’s going to call a BAL method like GetAllBlogPosts() or GetAllBlogPostsForCategory(). Last time I mentioned (over and over) that I like to keep things simple. When I need to get or save data, I don’t want to have to search through 3 different classes just to find the one with the method I need. Instead, I’m putting all my persistence logic for a given aggregate in just one place, a service class. This is not a web service. I’m using service in the Domain Driven Design sense here. That means that I have a BlogService class that is my one stop shop for all persistence that has to to with Blogs, BlogPosts, SubmittedBlogUrls, and anything else that falls within the Blog aggregate. Here is what my BlogService class looks like. You can see that it’s mostly “Get” data access methods.
What’s an Aggregate?
I keep using the word aggregate. If you’re not familiar with the term, it just means a group of entities that all share the same persistence class (whether that be a repository, a service, or something else). This is a key concept in Domain Driven Design. If you want to know more I would recommend picking up Eric Evans’ book or Jimmy Nilsson’s book on DDD. For now, all you need to know is that a BlogPost can never exist without a Blog, so there’s no point in BlogPost having it’s own persistence class. In fact we find that if we do break BlogPost out into it’s own persistence class, it will lead to problems down the road due to BlogPost’s dependency on Blog. What’s the solution? We put data access methods for both Blog and BlogPost in the same persistence class and call it an aggregate. That is why BlogService has methods for both Blog and BlogPost entities.
What type of data will data access methods return?
We covered this last post, but to recap all data will be returned as a Data Transfer Object (DTO). The DTOs are all defined in our Common assembly in the DataShapes folder. Our BAL will return data in one of the following 4 formats.
- a single DTO
- a List<DTO>
- a DataPage<DTO>
- a string value
For more see last week’s post Agile ADO.Net Persistence Layer: Part 2 Use DTOs.
A simple Single<DTO> data access method
Let’s look at the simplest possible data access method. GetBlogPost() takes a postGuid for a parameter, defines the query to find the BlogPost entity for that postGuid, and then returns the result as a single BlogPost DTO. Here’s the complete method.
public BlogPost GetBlogPost(Guid postGuid)
{
string query = @"SELECT p.*, s.Score
FROM [dbo].[BlogPost] p
LEFT JOIN [dbo].[BlogPostReputationScore] s on s.PostGuid = p.PostGuid
WHERE PostGuid = @PostGuid";
SqlDao dao = new SqlDao();
SqlCommand command = dao.GetSqlCommand(query);
command.Parameters.Add(dao.CreateParameter("@PostGuid", postGuid));
return dao.GetSingle<BlogPost>(command);
}
The first thing you’ll notice is that this isn’t a lot of code. All we’re really doing here is defining a parameterized TSQL query, wrapping that query up in a SqlCommand, and then passing the command and our desired return type off to a Data Access Object (DAO) that automagically executes the command and maps the results to our desired type. It may seem counter intuitive to write code like this when we haven’t even written the DAO yet, but that’s exactly how I did it when I wrote this code for the very first time. I decided that my data access methods should be very simple. I would start with the query and the DTO type that I wanted it to return, then I would pass them both to some type of helper class that would handle the details of running the query and figuring out how to map the query results to the properties of my DTO. By using this top down approach, I gave myself a very clear picture of how I needed my DAO to behave.
What’s a DAO (Data Access Object)?
By looking at the query logic above, you can see that I have this thing called a DAO or Data Access Object. This is a class that encapsulates the helper logic for working with my database. The DAO handles things like creating parameters, getting a connection, and most importantly it implements methods to return my four main data formats, GetSingle<DTO>, GetList<DTO>, GetDataPage<DTO>, and GetStringValue(). The DAO and it’s associated DataMappers are where you’ll find the special sauce that makes this architecture work. We’ll get into their implementation later on.
A BAL that embraces change
It’s easy to look at the simple code above and miss something that I think is very important. In fact that thing is the whole reason that I wrote this framework. That simple data access method above is the blueprint for a flexible persistence layer that makes changing your entities and associated persistence code easy and almost painless. It sets up a simple 3 step process for all data access in your application.
- Define a DTO in the exact data shape that you’re looking for. That means create a DTO property for each data field that you need out of the database.
- Define a query that gets the data. It can be as simple or as complex as you like. You can develop it in Sql Server Management Studio. You can easily optimize it. Use whatever process or tools work for you. When you’re done just paste the query into your data access method.
- Pass both the query and your DTO to the DAO and it will automatically handle field mappings and pass the results back in the data shape you requested.
This is a very powerful way to work. I can’t count the number of times that I’ve worked with and architecture where I dreaded any changes because I knew that any data fields added would require me to modify a sproc, a DAL method, a BAL method, parsing logic, an entity class, it all adds up to a lot of friction that resists any change. This BAL design embraces change. It’s written with the attitude that we know change is going to happen so we’re going to give you as few things as possible to modify, and make sure we don’t have any cross cutting dependencies, so that you can make changes easily.
Next time, more on the service classes.
Next Post: Agile ADO.Net Persistence Layer Part 4: Writing data access for new data shapes