The data layer is where the persistence code goes. In most programs, we need some kind of persistence to store our application data, which is often a database. Several patterns come to mind when discussing the data layer, including the Unit of Work and Repository patterns, which are very common. We cover these two patterns very briefly at the end of this subsection.We can persist our domain model as is or create a data model that is more suited to be stored. For example, a many-to-many relationship is not a thing in the object-oriented world, while it is from a relational database standpoint.You can view a data model like a DTO for the data. The data model is how the data is stored in your data store; that is, how you modeled your data or what you have to live with.In a classic layering project, you have no choice but to have a data model. However, we explore better solutions as we continue to explore additional options.
An ORM is a piece of software that translates objects into a database language such as SQL. It allows mutating data, querying data, loading that data into objects, and more.
Modern data layers usually leverage an ORM such as Entity Framework Core (EF Core), which does a big part of our job, making our lives easier. In the case of EF Core, it allows us to choose between multiple providers, from SQL Server to Cosmos DB, passing by the in-memory provider. The great thing about EF Core is that it already implements the Unit of Work and the Repository patterns for us, among other things. In the book, we use the in-memory provider to cut down setup time and run integration tests.
If you’ve used EF6 before and dread Entity Framework, know that EF Core is lighter, faster, and easier to test. Feel free to give it a second shot. EF Core’s performance is very high now too. However, if you want complete control over your SQL code, look for Dapper (not to be confused with Dapr).
I don’t want to go into too much detail about these patterns, but they are important enough to deserve an overview. As mentioned, EF Core already implements these patterns, so we don’t have to deal with them. Moreover, using such patterns is not always desirable, can be hard to implement right, and can lead to bloated data access layers, but they can also be very useful when used well.
I’ve written a multi-part article series about the Repository pattern. See the Further reading section.
In the meantime, let’s at least study their goals to know what they are for, and if the situation arises where you need to write such components, you know where to look.