Sql – repository pattern with a legacy database and Linq to SQL

ddd-repositoriesdomain-driven-designlinq-to-sql

I'm building an application on top of a legacy database (which I cannot change). I'm using Linq to SQL for the data access, which means I have a (Linq to SQL) class for each table.

My domain model does not match with the database. For example, there are two tables named Users and Employees, and therefore I have two Linq to SQL classes named User and Employee. But in my domain model I'd like to have a User class which should contain some fields from either table (but I don't care about a lot of the other fields of these tables).

I'm not sure how I should design my repositories:

  • should the repositories perform the mapping between Linq to SQL classes (e.g. User, Employee) to the domain classes (User) and only return the domain classes to the application
  • or should my repositories return the Linq to SQL classes and leave the mapping to the caller

The first approach seems to make more sense to me, but is this the correct way to implement my repositories?

Best Answer

The purist (I try to stay pure) will tell you that your model represents your data. And therefore, anything that needs to be persisted is done so only when needed through repositories. Also, when you have complex entities, you want to use a service to combine them. For example, User + Employee = UserEmployee entity that is only accessible through an IUserEmployeeService.

With those vague statements, you have an excellent opportunity here.

Build an anti-corruption layer, which allows you to start moving off of the legacy DB at the same time.

This is an another chapter in the DDD playbook. An Anti-Corruption layer is used to interface with a legacy system using Facades, Translators, and Adapters to isolate the legacy DB with your pure Domain model.

Now, this may be a lot more work than you wanted. So, you have to ask yourself at this point:

Do I want to start the process of moving off of this legacy DB, or will it remain for the life of the application?

If your answer is you can start migrating, then model your actual domain the way you want it. Persist it with normal repositories and services. Have fun designing it the way YOU want it stored. Then, use the services of the aggregate roots to reach into the anti-corruption layer and pull out the entities, store/update them locally, and translate into your domain's entities.

If the answer is that the legacy DB will remain for the life of the project, then your task is much easier. Use your domain's services (e.g. UserEmployeeService) to reach into the anti-corruption's UserFacade and EmployeeFacade (similar to a "Remote Service" concept).

Within the Facades, access the legacy db using the Adapters (e.g. LegacyDbSqlDatabase) to get a raw legacyUser(). The next step would be to use an UserTranslator() and EmployeeTranslator() mapper that converts the legacy user data into your actual domain's version of the User() entity, and return it from the UserFacade back to your UserEmployeeService, where it is combined with the Employee entity that came from the same place.

Whoa, that was a lot of typing...

With your Adapters and Facades of your Anti-Corruption layer, you can do your Linq-to-Sql or whatever you want to do. It doesn't matter because you have completely isolated the legacy DB/system away from your nice and pure Domain - your domain that has its own version of User() and Employee() entities and value objects.

Related Topic