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:
- In a repository, should I return IQueryable or IEnumerable?
- In a service or repository, should I do something like..
GetCustomerByLastName
,GetCustomerByFirstName
,GetCustomerByEmail
? or just make a method that is something likeGetCustomerBy(Func<string, bool> predicate)
?
Best Answer
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.
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 anIQueryable
.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?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 calledRepositoryBase<T>
which exposedprotected T GetByPredicate(Func<T, bool> predicate)
which was only used by concrete repositories (eg,public class CustomerRepository : RepositoryBase<Customer>
) then that would be fine.