Repository<T>, Syntactic Sugar and DDD
I am toying around with several ideas about the use of Repository<T> in DDD context. Right now it is theoretical because my current project has little need for DDD.
At the moment, I am using the static access for Repository<T> as my gateway for the repositories.
1: public class OrdersController : BaseController
2: {
3: public void CalculateTaxes(int customerId)
4: {
5: Customer customer = Repository <Customer>.Load(customerId);
6: ICollection<Order> orders = Repository<Order>.FindAll(Where.Order.Customer == customer &&
7: Where.Order.Status == OrderStatus.ReadyToShip &&
8: Where.Order.Date >= DateTime.Today);
9:
10: Employee lowestEmployeeResponsibleForCustomer =
11: Repository<Employee>.FindFirst(Where.Employees.Customers.Contains(customer),
12: OrderBy.Employee.Level.Asc);
13:
14: foreach (Order order in orders)
15: {
16: customer.ApplyDiscount(order);
17: lowestEmployeeResponsibleForCustomer.Dispatch(order);
18: }
19: }
20: }
Another style may utilize a base controller as the place to put the repositories, like this:
The resulting code looks like this:
1: public class OrdersController : BaseController
2: {
3: public void CalculateTaxes(int customerId)
4: {
5: Customer customer = Customers.Load(customerId);
6: ICollection<Order> orders = Orders.FindAll(Where.Order.Customer == customer &&
7: Where.Order.Status == OrderStatus.ReadyToShip &&
8: Where.Order.Date >= DateTime.Today);
9:
10: Employee lowestEmployeeResponsibleForCustomer =
11: Employees.FindFirst(Where.Employees.Customers.Contains(customer),
12: OrderBy.Employee.Level.Asc);
13:
14: foreach (Order order in orders)
15: {
16: customer.ApplyDiscount(order);
17: lowestEmployeeResponsibleForCustomer.Dispatch(order);
18: }
19: }
20: }
I really like the Repository<T> static accessor for stuff like tests that requires data setup, but I think that using it in the controllers may be over doing it. I like the second approach better, because the syntax doesn't have <T> all over the place, as well. It is also nicer from the point of view of syntax and it allows to replace the current IRepository<User> with IUserRepository very easily.
Thoughts?
Comments
Given that I am still very new to understanding DDD, I'm curious to wonder why the repository (interface or otherwise) isn't abstracted behind either a reusable query or some sort of "service" object? The fact that the abstraction could be viewed as over-designin the canonical "Customer with Orders of Products" sample, a non-trivial application could/would benefit in a specific query (Specification pattern?) object that would delegate to the Repository; or the encapsulation of many reusable queries and other actionable operations within a Service object? To apply this to the given example, an OrderService that, when a customer completes an order, updates the customer's record such that their "rewards account" reflects with their most recent purchase.
Ignoring, for the moment, that my example may violate Single Responsibility/Separation of Concerns, would such an application be valid with regards to the Repository<T>?
Your blog is a wealth of information. Thanks so much for contributing to the community in all the ways you do!!!
Well, what do you call with?
Where.Order.Customer == customer && Where.Order.Status == OrderStatus.ReadyToShip &&
Where.Order.Date >= DateTime.Today
To me this is a specification, and for the complex ones I would move it to the repository, for SoC.
I have a GenericRepository<T> class that holds all common methods like, GetById, GetByQuery, Save, SaveOrUpdate and just wraps the NHibernate session object.
I also have classes that derive from GenericRepository and implement an interface that lives in my domain model.
ie.
public class ItemRepository : GenericRepository<Item, int>, IItemRepository
Then any specific functionality to that repository I can place in ItemRepository and inherit the rest from GenericRepository. The second type parameter denotes what type the PK is.
I've only used it on two projects but I like how it works thus far. I can't take complete credit though, I originally saw how Bill McCafferty had a tutorial setup on thecodeproject and used that as a guide. I felt alot of his genercrepository class had a little too much unneeded functionality.
I also use a a generic wrapper around the session object and derive my entity managers from this wrapper.
I don't like the way that you define your managers in your base class. Seems like it should just be but in the OrdersController as datamembers. Employees and customers arent managed by the orderscontroller so it would be weird if it could access these through its base class.
Better use a kind of dependency injection or factory pattern to initialize these at construction/initialisation of your ordercontroller.
I am thinking about defining them in the base class because that makes them easily accessible for all controllers. Just a way to reduce the amount of things that you need to do.
Why don't you just define a new class that contains properties that make the typed managers available and use that from the controller class?
Because then I would need something like: Managers.Users, no?
Cut the middle management, I say :-)
I think that is a great approach especially when using dependency injection for those repositories.
craig
I second Ramon on not defining managers in a base class. It's kind of code smell and makes your controllers fragile and dependent on the base implementation. Given the fact I'm using IoC to retrieve my controllers I personally don't find it a burden for me to generate all the code for member managers using R#. Indeed, just type in something like
IRepository<User> _repository;
then Alt-Ins -> generate read properties, generate constructor. That's it.
With a large domain you could end up with a lot of properties cluttering up your BaseController.
Also as a side note, wouldn't this play havoc with the debugger when you quick watch your controller? I would need to resolve all of your properties which would hit your container? Not really a big deal but I know that you already punish devenv mercilessly :)
I don't like statics anywhere, but I can live with them in cases like SystemClock as a wrapper for service-locating my ISystemClock instance when I do time-sensitive calculations in my domain objects ( or even when setting LastUpdated). Beyond that, I like to use Interface fields.
I prefer to pass in IMyRepository to the constructor of classes that need it.
Comment preview