4
votes

When I try to use DDD with .NET and C# my usual procedure is to construct one project for the Domain Model, another one for Data Access and another one for the application itself, like ASP.NET Web API. Usually there are more projects involved, but these three I usually work with.

My point here is that in the Domain Model project I put all business rules. There I define the Domain types (entities, value types, aggregates and so forth) and the repository interfaces.

My point then is that after I define the repository interfaces I implement then in a separate project. That project is the Data Access project. My reason for that is that to implement the data access I usually need technologies which do so (like ADO.NET, EF, Dapper and others).

I myself find it bad form to couple those dependencies on the Domain project for some reasons. I like the Domain project to be self contained and decoupled from any technologies.

On the other hand, I've heard sometimes that repositories might contain business rules and should be implemented inside of the Domain project.

Which approach is right? My approach which decouples the repositories implementation from the domain project in order to avoid coupling dependencies on technologies to the domain project or the approach which allows business rules inside repositories and thus makes repositories live inside the Domain project?

Where repositories really should be? In the domain project together with the domain types or in a Data Access project?

4

4 Answers

3
votes

Your approach is correct. Repositories should be implemented in infrastructure layer.

On the other hand, I've heard sometimes that repositories might contain business rules and should be implemented inside of the Domain project.

The only responsibility of a repository should be persistence. If you end up with business logic in repositories, you should refactor them. Move the business logic to the domain model and keep the repositories in the infrastructure.

If you have business logic in queries, an easy way to deal with it in C# is to return IQueryable<T> from repositories. Then you can use LINQ to create queries in application or domain services. Some might argue that it is a leaky abstraction - you limit the implementation of repositories to data access technologies that work well with IQueryable. The ease of use very often makes it worth a try.

1
votes

Repositories are the boundary that mediates between the domain and the data mapper. I would say that repository isn't about data access but just who translate the domain into some kind of data using the data mapper and also it does the inverse translation from the data to domain.

Instead of defining and implementing them outside the domain projects, if you want to leave them as tech-agnostic, maybe you can define repository interfaces on the domain project and create a Domain.[SomeTech] where SomeTech may be EF, Dapper, and so on, and where you're going to implement tech-specific repositories.

Now your application project will need to reference both Domain and, for example, Domain.EF projects.

BTW, based on my own experience, a good separation of concerns in terms of how the code files are organized is less important than how the solution is architected in terms of software layers.

That is, if your project is entirely using an OR/M like Entity Framework, the chance that you change it in the future is insignificant, thus, don't waste your time creating a solution of too many projects with few classes each them and focus yourself on a good separation of concerns in your code.

There's always time to split your projects, while a wrong architecture is just a waste of time.

1
votes

Keep in mind that every codebase, every project, every domain is different. There are no hard-and-fast rules. Design the code to best fit what works for your project.

I've heard sometimes that repositories might contain business rules

If that's the case then they're not merely repositories. In a strictest sense, a repository just persists things to some storage.

Now, there can be business-driven logic inside the repositories which is still just persistence logic. And it's possible that this persistence logic would, in the standard 3-layer setup you describe, need to be duplicated in any other implementation of that layer. That's not the end of the world.

But it does beg the question that you're asking. And, as I said, there really are no hard-and-fast rules. You would need to decide for your project which rules you want to maintain and design the code around those rules.

For example(s)...

  • You might move this "business logic" into the models or other domain code and keep the repositories simple.
  • You might keep this "business logic" in the repositories and re-implement it in other repository implementations.
  • You might create a thin layer between the traditional domain and the simple repositories which applies this "business logic". You can think of these as domain-owned wrappers to the repository interfaces. The sole purpose of these objects, which aren't really business models but are conceptual logical types related to the repositories, is to contain specifically this logic.

In the end, it's really up to you. There is no right answer except the answer which makes the most sense for your codebase and results in simple, maintainable code.

1
votes

There should be a logical separation in your representation of the Solution [*1] between those objects representing data management, human interaction, system interaction, and your Problem Domain (problem object model).

Physical separation (into say Projects) is entirely optional. Many would argue that it is a good organizational practice.

Data management objects (e.g. repositories) should only have functionality to persist and retrieve persisted data, not business logic or rules.

[*1] Includes both objects that model your problem (the "Problem Domain"), as well as all the supporting objects required to produce a complete working system.