C# – in DDD, should repositories expose an entity or domain objects

cdomain-driven-designrepository-pattern

As I understand it, in DDD, it is appropriate to use a repository pattern with an aggregate root. My question is, should I return the data as an entity or domain objects/DTO?

Maybe some code will explain my question further:

Entity

public class Customer
{
  public Guid Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

Should I do something like this?

public Customer GetCustomerByName(string name) { /*some code*/ }

Or something like this?

public class CustomerDTO
{
  public Guid Id { get; set; }
  public FullName { get; set; }
}

public CustomerDTO GetCustomerByName(string name) { /*some code*/ }

Additional question:

  1. In a repository, should I return IQueryable or IEnumerable?
  2. In a service or repository, should I do something like.. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? or just make a method that is something like GetCustomerBy(Func<string, bool> predicate)?

Best Answer

should I return the data as an entity or domain objects/DTO

Well that entirely depends on your use cases. The only reason I can think of returning a DTO instead of a full entity is if your entity is huge and you only need to work on a subset of it.

If this is the case, then maybe you should reconsider your domain model and split your big entity into related smaller entities.

  1. In a repository, should I return IQueryable or IEnumerable?

A good rule of thumb is to always return the simplest (highest in the inheritence hierarchy) type possible. So, return IEnumerable unless you want to let the repository consumer work with an IQueryable.

Personally, I think returning an IQueryable is a leaky abstraction but I've met several developers who argue passionately that it isn't. In my mind all querying logic should be contained and concealed by the Repository. If you allow calling code to customise their query then what's the point of the repository?

  1. In a service or repository, should I do something like.. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? or just make a method that is something like GetCustomerBy(Func predicate)?

For the same reason as I mentioned in point 1, definitely do not use GetCustomerBy(Func<string, bool> predicate). It may seem tempting at first, but this is exactly why people have learned to hate generic repositories. It's leaky.

Things like GetByPredicate(Func<T, bool> predicate) are only useful when they're hidden behind concrete classes. So, if you had an abstract base class called RepositoryBase<T> which exposed protected T GetByPredicate(Func<T, bool> predicate) which was only used by concrete repositories (eg, public class CustomerRepository : RepositoryBase<Customer>) then that would be fine.