2

I am considering how to write a repository for a new project.

I like the idea of a generic repository like this for the basic CRUD operations:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity

However, I also read that this is against the spirit of DDD. Therefore as a compromise I was thinking about creating a generic repository and then a few repositories that inherit from it e.g. CustomerRepository: Repository etc. However, I cannot find any documentation to support this idea.

Q1) Is it a reasonable idea to create a base repository?

Also, I am trying to create one repository per aggregate root rather than one repository per entity. Say I have two classess i.e. Order and OrderItem (Order is the aggresgate root), then:

Q2) Is it reasonable for all the data access for the OrderItem to go in the Order repository (the aggregate root)? i.e. SELECT * FROM OrderItem WHERE OrderID=@OrderID (translates to LINQ).

  • Just don't do it... repositories are collections of global variables. – Frank Hileman Jul 31 '17 at 22:47
  • @Frank hileman, what is the alternative to a repository? – w0051977 Jul 31 '17 at 23:07
  • 1
    Isn't the database itself a giant set of global variables? Why would you want more than one instance of, say, a Customer object, when there is only one customer record? – John Wu Jul 31 '17 at 23:20
  • 1
    No I am saying the argument that "repositories are collections of global variables" is questionable, seeing that the very purpose of the repository is to represent a collection of global variables. – John Wu Jul 31 '17 at 23:24
  • 5
    @Frank Sounds like you've got some poorly structured code. If the same record needs to be worked on by more than one function, they should have retrieved the domain object from the (static) repository then passed it between functions, rather than re-retrieving it from the repository each time. Or better yet, encapsulate the scattered logic into the domain object. Eliminating the repository wouldn't be my first choice for refactoring. – John Wu Aug 1 '17 at 2:39
5

In Domain Driven Design, the repository is the boundary between the application and the domain model. It's a role interface used by the program to access the aggregate roots (themselves role interfaces, giving you access to specific entry points in your domain model).

As a rule, you want the interface between the application and the domain model to be as explicit as possible, so that you can optimize for each particular use case.

The fundamental problem with generic repository is that, with no information about which state is relevant, you have to produce all of it.

Udi Dahan offers a nice example of creating a repository that informs the implementation of how the state is going to be used.

That said, if you want to use a generic implementation behind the scenes, and introduce specialized implementations only when you know that the benefits out weight the costs... that's perfectly fine.

Udi's essay also raises an interesting point: the explicit interface puts into the code a better description of what is going on, where a generic interface is more learnable.

This may seem like a lot of trouble to go to. It actually isn’t. It’s pretty much just a different way to package the code you already wrote. Yes, there are more interfaces, and probably more classes, but the amount of business logic code is the same. I’ve been able to keep performance high with this design, but increase its maintainability. I measure maintainability as both the amount of time, and number of changes that need to be made by a programmer familiar with the design. Learnability (?) is often called maintainability, but I think that it’s something else. This design may not be as learnable– meaning it would take a given programmer longer to learn this design than the previous one. I submit that the increased maintainability outweighs the increased learnability substantially.

3

A1) Usually not. Consider a concrete CustomerRepository. It might access data in an SQL database, a document database, directly in-memory etc. It might be beneficial to name it after the data store technology it relies on, e.g. MongoCustomerRepository. To abstract the concrete data access technology and achieve easier testing, one would create an ICustomerRepository which the concrete CustomerRepository would implement. Creating an IRepository as a base repository interface then would be abstracting an abstraction. Because of this, it wouldn't serve much benefit besides saving a few lines of repeating interface code and could potentially confuse users.

One similar idea is to build a generic base repository which exposes query methods that take in for example a LINQ expression or another form of a query. Besides abstracting abstractions, these almost always end up being leaky in that one cannot properly abstract away the query language a concrete implementation might use. And one shouldn't, since queries of ORM Frameworks and database drivers are fully featured and will be hidden behind an interface anyway.

A2) Repository interfaces should be aligned according to business use cases. Furthermore, entities and value objects can only have references to objects inside the same aggregate and other aggregate roots. This is so that the aggregate root can ensure consistency and proper validation of business rules inside the aggregate. Therefore, if an Order is the root of an aggregate which contains OrderItem value objects, it's inappropriate to return OrderItems directly from a repository. Instead, one should return an Order that knows how to satisfy use cases involving its OrderItems.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.