2

Ok it seems my project setup could use some improvments.

I currently have:

1. ASP.NET MVC3 Web project
2. NHibernate project with Repositories/Mappings and some session code.
3. Entities (models used in nhibernate like User.cs)
4. Interfaces (like IUser, IRepository<IUser>, IUserRepository...)
5. Common (UserService, ..)

Now the issue is that I my nhibernate models now need to implement IUser, which I don't like, but I was forced to do this since my IRepository is generic, and I could use IRepository<User> since User is in another project, so I had to create an interface and do IRepository<IUser>

I will never need to have another implemention of User, so this is bugging me.

How can I fix this while keeping things seperate so I can swap out my ORM?

Blankman
  • 259,732
  • 324
  • 769
  • 1,199
  • 2
    Why would you want to swap ORM later? Unless it is core requirement it is complete nonsense. Designing application toward this target is hard and complex so it should be done only if it is required **now**. – Ladislav Mrnka Sep 18 '11 at 11:18
  • 2
    @Ladislav Mrnka, the fact that you might never swap an ORM is not a reason to properly architect the application with correct separation of concerns in mind. Also he might get tired of NHibernate. `Dapper` and similar ORMs are pretty modern nowadays :-) – Darin Dimitrov Sep 18 '11 at 11:19
  • 2
    @Darin: And where is a boundary between properly architected and over architected? That is not about separation of concerns - using NHibernate directly can still follow separation of concerns without any problem. – Ladislav Mrnka Sep 18 '11 at 11:20
  • Define *over architected* and I will answer you :-) In the answer I provided I defined what IMHO is *properly architected*. – Darin Dimitrov Sep 18 '11 at 11:21
  • 1
    @Darin: IMHO it is "properly architected" but not as a blue print. It is properly architected only in very special scenario when you follow domain driven design and your repositories work with aggregates - not with separate entities. Otherwise whole repository layer is redundant because it just wraps what NHibernate provides out of the box. Changing data access layer is much more complex than reimplementing some interface - it is big architecture change and it will in most cases affect architecture of the whole application anyway. – Ladislav Mrnka Sep 18 '11 at 11:30
  • 1
    @Ladislav, actually I don't define a separate repository per domain entity, I define a repository per aggregate domain root. – Darin Dimitrov Sep 18 '11 at 11:33
  • what is a aggregate? confused here! – Blankman Sep 18 '11 at 11:48
  • @Ladislav: 'swap ORM' is hard to achieve in reality and may not be needed now or ever for OP. But he will end up with a better, persistent ignorant, design if he approaches this problem **as if** ORM swap is required. Spreading NHibernate/EF specific types all over the code is not an answer. – Dmitry Sep 18 '11 at 13:26
  • 1
    @Blankman: [Aggregate roots](http://stackoverflow.com/questions/1958621/whats-an-aggregate-root) are key concept of Domain driven design. The rule of thumb is that you have repository only per aggregate root (= no repositories for dependent entities). That is one of cases where using repositories make sense. Sure not every entity type must have dependent entities. – Ladislav Mrnka Sep 18 '11 at 13:52

2 Answers2

1

The IUser interface must be defined in the Entities layer if your entities implement it, not in the Interfaces layer. Also I would probably rename this generic Interfaces layer to Repositories or AbstractRepositories or something. Also I would rename the Common layer to Services if it contains services aggregating your repositories.

So the picture could be:

  1. ASP.NET MVC3 Web project
  2. NHibernate project with Repositories/Mappings and some session code.
  3. Domain Entities (models used in nhibernate like User.cs and implementing domain interfaces like IUser)
  4. Repositories (like IRepository<IUser>, IUserRepository...)
  5. Services (UserService, ..)
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Actually I also wanted to drop the idea of having interfaces for my main model objects like IUser, I don't need that really. – Blankman Sep 18 '11 at 11:49
  • @Blankman, yeah you don't need them. Feel free to remove them. – Darin Dimitrov Sep 18 '11 at 11:51
  • what about the interfaces for IUserService, where should they go, in the services project? The reason I was thinking of seperating them is if i wanted to re-implement, I could just create another services project and implmenet the interfaces from my interfaces project. – Blankman Sep 18 '11 at 14:57
  • @Blankman, the Services layer seems like a good place to have the interfaces and the services. You could of course externalize them into a separate assembly if you want (something like ServiceContracts). – Darin Dimitrov Sep 18 '11 at 14:58
  • Do I really need a seperate assembly for repositories, I mean they are very tighly coupled with nhibernate no? Unless the contract of the interface is re-usable...struggling with this since I don't know much about EF. – Blankman Sep 18 '11 at 18:58
  • 1
    @Blankman, you don't really need separate assemblies for any of your functional layers. You could put everything in the same assembly which is the ASP.NET MVC application. Just make sure to separate them in namespaces. Assemblies are just a physical boundary but should not be regarded as the only way to organize a weakly coupled layers of your code. – Darin Dimitrov Sep 18 '11 at 19:00
  • 1
    The best use case I've found for providing an interfaces assembly is for WCF services, and publicly facing APIs. If you're going to ship it to the customer separately, it is good to put it in its own assembly. If not, then it is up to your discretion. Doing so might give you a bonus of smaller test case deployments, and might help enforce separation of architecture layers for your lazier or less informed devs on the team, but gives you a (slight) complexity hit. – Merlyn Morgan-Graham Sep 18 '11 at 22:19
1

I think you should approach this problem from Domain Driven Design perspective. Domain should be persistent-ignorant. Proper implementation of DDD repository is a key here. Repository interface is specific, business-focused, not generic. Repository implementation encapsulates all the data access technicalities (ORM). Please take a look a this answer and these 2 articles:

Your entities should be concrete types, not interfaces. Although you may never need to swap your ORM (as Ladislav is saying in comments), you should design it as if you will need to swap it. This mindset will really help you achieve persistence ignorance.

Community
  • 1
  • 1
Dmitry
  • 17,078
  • 2
  • 44
  • 70
  • That mindset is not always correct - you cannot apply it as a rule. In most cases it is enough to have persistence ignorant domain model. Not a service layer. Having service layer persistence ignorant make sense if you want to swap ORM but in that case repository will not save you - it takes much bigger effort to implement repositories, unit of work, specifications etc. to build serious abstraction. You also don't have to allow any ORM side effect like lazy loading or change tracking to bubble to your upper layer. – Ladislav Mrnka Sep 18 '11 at 13:45
  • As a side note I have seen several projects building such abstractions (I was working on some of them) and at the end the only result of the effort put in the abstraction was harder usage of ORM because of intermediate layer hiding some ORM specific features and serious problems with deadline because too much time was spent on abstraction which didn't provide any real value except some theoretical correctness for architecture purist. It was time when I change my mind and become more pragmatic. – Ladislav Mrnka Sep 18 '11 at 13:48
  • Not following you. Repository implementation hides 99% of ORM API behind domain driven interface. UnitOfWork hides the rest 1%. In NHibernate case UnitOfWork implementation is under 50 lines of code. None of the code in the system (services, UI etc) would reference ORM directly. I designed relatively large system like that. It is highly unlikely that ORM swap will ever be needed there but we achieved high degree of persistence ignorance by designing 'as if ORM will be swapped'. This approach also made unit and integration testing a lot easier. – Dmitry Sep 18 '11 at 13:59
  • I'm also not following you. NHibernate has well defined API which offers testability (in contrast to Entity Framework) so if you never expect to swap ORM what is the value of that abstraction and high degree of persistence ignorance? I'm looking for a real value - solution to some problem, making something possible, etc. Design by book is not a value. Also how your repositories hides ORM API? Is it by methods like GetXXXByYYY or GetXXXForYYY? IMHO in modern architecture this type of data access objects is obsolete - it is from stored procedure era. – Ladislav Mrnka Sep 18 '11 at 14:36
  • "NHibernate has well defined API which offers testability". Testability part is simply not true. API is vast (Session, Criteria, HQL etc) and is out of your control, i.e. it is external and you don't own it. So isolating your code by mocking this API for testing purposes is a waste of time. I went into details about this issue here: http://stackoverflow.com/questions/7110981/the-repository-itself-is-not-usually-tested/7111748#7111748 – Dmitry Sep 18 '11 at 15:12
  • Repository interface exposes business-centric methods like repo.GetDelinquentOrdersForCustomer(x) or repo.GetForCustomer(delinqSpec). The fact that ORM is used to implement this method is hidden. Why would you call this method 'from stored procedure area'? – Dmitry Sep 18 '11 at 15:20
  • Ok +1. I see your point now - I like the part with mocking API you don't own in your linked answer - it is actually what I always recommend when working with EF but I didn't think about it globally. My comment about stored procedure era was targeted to repositories exposing great amount of methods without using specifications - I think your repositories use specifications. Still it doesn't mean I would take abstraction as a must. – Ladislav Mrnka Sep 18 '11 at 15:46