2

This is my first attempt, after reading a lot about DDD, TDD and Repository / UnitOfWork paterns, to make my own application.

I'm using Entity Framework, MVC 4 on .NET 4.0 (the server that will be running this application is a Windows 2003)

This is the basic simplified pattern logic (The original one uses a IRepository, IUnitOfWork, GenericRepository and extends the EF POCOs with a IEntity interface to give access to the common ID field. But this simplified example will be enough to ask my question)

View -> ViewModel -> Controller <- UnitOfWork <- Repository <- EntityFramework <- Database

View

Model.Employee.GetSeniority()

EmployeeDetailsViewModel

Employee e { get; set; }

Employee

DateTime dateHired { get; set; }
TimeSpan GetSeniority()
{
    return DateTime.Today - dateHired;
}

Controller EmployeeDetails()

using(var unitOfWork = new UnitOfWork) {
    return View(EmployeeDetailsViewModel model = new EmployeeDetailsViewModel {
        e = unitOfWork.GetEmployeRepository().Find(o=>o.id == id)
    });
}

UnitOfWork GetEmployeRepository()

return (_employeeRepository ?? _employeeRepository = new EmployeeRepository(this.dbContext));

Repository Find()

dbContext.Configuration.EnableProxyCreation = false;
Employee e = dbContext.Employees.Where(expression);
dbContext.Configuration.EnableProxyCreation = true;
return e;

Everything actually works properly. The problem is that I feel like something is terribly wrong here, and I'm not sure at which layer it should be fixed.

After getting suggested, by a lot of people (Hi Darin), to always pass ViewModels to views and no models, I started doing this. However, what I'm doing (I think) isn't much better. I'm simply encapsulating my model in a viewmodel. At first, it didn't sound that bad since my Find() method would turn proxy off before getting an object, which would result in a persistance-ignorant POCO. However, now that I want to add some logic in the POCOs, I feel like there is something wrong.

I think the problem resides in where my Business logic is and the fact that my Employee POCO should be mapped to a DTO object. However, where should I transfer the Employee POCO to a EmployeeDTO? Should that be the task of the Repository, the Controller or something else? I'm also unsure of where I should put my business logic (as simple as GetSeniority() displayed in the example). Should that be added to the EF POCO through partial classes or should that be in the DTOs? Or is there another missing step in the Employee -> EmployeeDTO transfer?

Pluc
  • 2,909
  • 1
  • 22
  • 36

1 Answers1

3

This is a great question. It looks like you are trying to find clean separation which is fantastic. I would break the problem up. You have Data Access and you have UI display and in between you have your business logic. If you want to use a Domain model approach here is how I would structure it.

  • Never expose EntityFramework Entity classes outside of Repository. You can choose to return Dto's (POCO') or Domain objects from your repository. If you want Dto's for more separation, that is fine you will just need another layer such as a service layer to convert the Dto's into Domain Objects.

  • Put your business logic in your Domain Objects. So Domain.Employee.GetSenority() would be on your domain object.

  • Any logic that does not fit in your Domain Objects can reside in your UnitOfWork or Service Layer.

  • Convert Domain objects to ViewModel in controller. At this point map Employee.GetSenority() to MyViewModel.Senority property. Basically your ViewModel is a Dto and only contains view specific logic which typically is not much.

  • Where do you call the repository. You could use the UnitOfWork pattern as you have it or simply create a Service Layer class. The key here is that these should be useable to other application types. For instance if you were to write a desktop or Windows 8 Style App, you would likely want to reuse either of these along with your Domain Entities.

I'm sure you are having fun with this. Good luck.

Thomas
  • 3,532
  • 3
  • 20
  • 22
  • My DbContext is not exposed outside the Repository, so that is fine. However, you are saying DTOs are POCOs, but Entity Framework models are also POCO (at least in the version I'm using, as mentioned, they are POCOs with proxies which, with proxies turned off, becomes pure POCOs). What exactly should be considered Domain Objects in my senario? The Entity Framework object (POCOs)? Or DTOs? What exactly do you mean by "Convert Domain Objects to ViewModels? If I need a list of Employees in my ViewModel, do I need a ViewModel with a List of ViewModels? – Pluc Oct 16 '12 at 16:52
  • (cont.) The UnitOfWork lazy-load (or lazy-instantiate?) the repositories with a unique DbContext. The UnitOfWork is also a IDisposible implementation which, open disposable, will save the DbContext. This is the (I belive) most optimal way to use EF as it will do a single transaction after all modifications in that UnitOfWork instance. And making it a disposable just makes my code look sexy :) – Pluc Oct 16 '12 at 16:53
  • To me Dto's and POCO's are the same. You could consider Dto as a more specialized POCO but all they have is properties and if anything else helpers for converting data types or other basic non-business utility functions. To be more specific on the first bullet Entity Framework models should be converted to either a Dto/POCO or a Domain Object. You don't want something like IQueryable running loose outside of the Repository. When I say Convert Domain Objects to ViewModels it basically means take your Employee Object and any other needed Domain Objects and create a ViewModel object. – Thomas Oct 16 '12 at 18:16
  • You could simply use your Domain object as your view model and send it to the View, however, the Domain object doesn't always line up. Especially if you have multiple Domain Entities making up your view. The only real different between your Domain Entities and your Dto's is that they encapsulate your business logic specific to the entity. – Thomas Oct 16 '12 at 18:20
  • 1
    -Repository wraps EntityFramework -Repository returns and accepts Dto's -Unit of Work calls Repository and converts Dto's into Domain Objects -Controller calls Unit of Work and converts Domain objects into ViewModel - View "binds" to ViewModel for display. Caveat - You don't always need all of this. Depends on your app size and how much you want to reuse with other apps. – Thomas Oct 16 '12 at 18:22
  • There are different ways to make repositories and my repository does not wrap EntityFramework. UnitOfWork does. When I instanciate a UnitOfWork, it instanciate a DbContext. When you request a Repostory from the UnitOfWork, it will check of an instance in a dictionnary. If it doesnt find it, it instanciate a new one passing the DbContext as a parameter and returns that new repository. This allows all the repositories to work with the same instance of the DbContext. – Pluc Oct 16 '12 at 18:31
  • As for DTO / POCO / EF Models confusion, I got it sorted out. Thanks. – Pluc Oct 16 '12 at 18:32
  • I'm not going to reuse this with much other apps (if any), but this app is really huge and this data will be reused in various portions of the this app. That's why I'm spending a lot of time on the data structure and application design. – Pluc Oct 16 '12 at 18:34
  • One last precision - Something like "GetSeniority()" could then be considered a DTO "DataConversion" method? Since all it does it "(Departure Date ?? Current Date) - Hired Date"? If I add this logic in the domain object, I would have to add a read-only field for it in the DTO, and if the Hired Date change, I wouldnt be able to get that logic (in a correct way) back from the domain layer to adjust my DTO. So I FEEL like this would be a acceptable situation to add a method in a DTO object right? Or should I create a static extension method to do so? – Pluc Oct 16 '12 at 18:42
  • @Thomas Great answer. I just want to point out a common misconception about POCO's - they are *not* simple data structures. POCO's can encapsulate business behavior. Domain objects can (and should, IMO) be POCO's. POCO is your vanilla object, as opposed to an object that would be decorated with fancy attributes or inherit from a specific base class from a (oftentimes persistence-related) framework, but *not* as opposed to a rich domain object. In OO vanilla objects *do* have behavior. See http://en.wikipedia.org/wiki/Plain_Old_Java_Object – guillaume31 Oct 16 '12 at 20:54
  • @Guillaume31 - Thanks for the insight. I guess I fall into that misconception. Now I need to go back and read up on it. Specifically I need to read what the difference is between POCO and Domain object. It would seem that if POCO has the business logic then it would be the same as Domain Object. If you know the answer please post. – Thomas Oct 18 '12 at 13:47
  • @Pluc - With regards to DbContext. Yes I concur that the Dbcontext should be able to be created by UnitOfWork and passed in to Repository as you say. But in my opinion, you should never allow EntityFramework objects out of repository. That would let IQueryable out on the loose. – Thomas Oct 18 '12 at 13:50
  • @Thomas Not all Domain objects are POCO's, and not all POCO's are Domain objects. POCO and Domain object are 2 orthogonal concepts, which happen to intersect in the context of Entity Framework. There's an interesting definition of POCO here : http://stackoverflow.com/questions/2672409/what-is-entity-framework-with-poco – guillaume31 Oct 18 '12 at 14:21
  • @Pluc - With regards to where to put GetSeniority. Ultimately when reality sets in you don't always have to be so strict on every rule, however, you run the risk of one day opening your eyes and then going, yuck this is a mess so always try to keep everything in the "right" spot. To me while that is just a basic calculation, it is a business rule so it should not be in your DTO. I see your Domain object having DepartureDate, HireDate, and Seniority(readonly) properties. Your Dto will have DepartureDate and HireDate which get mapped over to the Domain Object. – Thomas Oct 18 '12 at 14:49
  • @Thomas, just to avoid confusion if people read these comments DTOs and POCOs are [not the same thing](http://stackoverflow.com/questions/725348/poco-vs-dto). POCOs can and usually do include behaviour/logic, DTOs don't. – Co7e Jul 29 '13 at 15:48