8

I seem to be missing something and extensive use of google didn't help to improve my understanding...
Here is my problem:
I like to create my domain model in a persistence ignorant manner, for example:

  1. I don't want to add virtual if I don't need it otherwise.
  2. I don't like to add a default constructor, because I like my objects to always be fully constructed. Furthermore, the need for a default constructor is problematic in the context of dependency injection.
  3. I don't want to use overly complicated mappings, because my domain model uses interfaces or other constructs not readily supported by the ORM.

One solution to this would be to have separate domain objects and data entities. Retrieval of the constructed domain objects could easily be solved using the repository pattern and building the domain object from the data entity returned by the ORM. Using AutoMapper, this would be trivial and not too much code overhead.

But I have one big problem with this approach: It seems that I can't really support lazy loading without writing code for it myself. Additionally, I would have quite a lot of classes for the same "thing", especially in the extended context of WCF and UI:

  1. Data entity (mapped to the ORM)
  2. Domain model
  3. WCF DTO
  4. View model

So, my question is: What am I missing? How is this problem generally solved?

UPDATE:
The answers so far suggest what I already feared: It looks like I have two options:

  1. Make compromises on the domain model to match the prerequisites of the ORM and thus have a domain model the ORM leaks into
  2. Create a lot of additional code

UPDATE:
In addition to the accepted answer, please see my answer for concrete information on how I solved those problems for me.

Community
  • 1
  • 1
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • Regarding 2 (default constructor), NH allows you to do constructor injection; I'm not sure about EF. Regarding 3 (complicated mappings), I'm not sure what you mean - if the object model doesn't closely match the relational model, there is inevitable complexity. Fluent NHibernate's conventions may help here. Regarding `virtual`, I don't like it either but it's painless. – default.kramer Nov 11 '11 at 21:43
  • Regarding 1 (virtual): you dont have to for non lazy loaded Entities. You can also change NH's requirement to wanting them – Firo Nov 19 '11 at 17:13

6 Answers6

5

In short - it is not solved

(here goes additional useless characters to post my awesome answer)

Arnis Lapsa
  • 45,880
  • 29
  • 115
  • 195
5

I would question that matching the prereqs of an ORM is necessarily "making compromises". However, some of these are fair points from the standpoint of a highly SOLID, loosely-coupled architecture.

An ORM framework exists for one sole reason; to take a domain model implemented by you, and persist it into a similar DB structure, without you having to implement a large number of bug-prone, near-impossible-to-unit-test SQL strings or stored procedures. They also easily implement concepts like lazy-loading; hydrating an object at the last minute before that object is needed, instead of building a large object graph yourself.

If you want stored procs, or have them and need to use them (whether you want to or not), most ORMs are not the right tool for the job. If you have a very complex domain structure such that the ORM cannot map the relationship between a field and its data source, I would seriously question why you are using that domain and that data source. And if you want 100% POCO objects, with no knowledge of the persistence mechanism behind, then you will likely end up doing an end run around most of the power of an ORM, because if the domain doesn't have virtual members or child collections that can be replaced with proxies, then you are forced to eager-load the entire object graph (which may well be impossible if you have a massive interlinked object graph).

While ORMs do require some knowledge in the domain of the persistence mechanism in terms of domain design, an ORM still results in much more SOLID designs, IMO. Without an ORM, these are your options:

  • Roll your own Repository that contains a method to produce and persist every type of "top-level" object in your domain (a "God Object" anti-pattern)
  • Create DAOs that each work on a different object type. These types require you to hard-code the get and set between ADO DataReaders and your objects; in the average case a mapping greatly simplifies the process. The DAOs also have to know about each other; to persist an Invoice you need the DAO for the Invoice, which needs a DAO for the InvoiceLine, Customer and GeneralLedger objects as well. And, there must be a common, abstracted transaction control mechanism built into all of this.
  • Set up an ActiveRecord pattern where objects persist themselves (and put even more knowledge about the persistence mechanism into your domain)

Overall, the second option is the most SOLID, but more often than not it turns into a beast-and-two-thirds to maintain, especially when dealing with a domain containing backreferences and circular references. For instance, for fast retrieval and/or traversal, an InvoiceLineDetail record (perhaps containing shipping notes or tax information) might refer directly to the Invoice as well as the InvoiceLine to which it belongs. That creates a 3-node circular reference that requires either an O(n^2) algorithm to detect that the object has been handled already, or hard-coded logic concerning a "cascade" behavior for the backreference. I've had to implement "graph walkers" before; trust me, you DO NOT WANT to do this if there is ANY other way of doing the job.

So, in conclusion, my opinion is that ORMs are the least of all evils given a sufficiently complex domain. They encapsulate much of what is not SOLID about persistence mechanisms, and reduce knowledge of the domain about its persistence to very high-level implementation details that break down to simple rules ("all domain objects must have all their public members marked virtual").

KeithS
  • 70,210
  • 21
  • 112
  • 164
  • Thanks for your answer, however, I think you are missing one option, basically the one I currently favor: Create the highly complex domain model without any knowledge about ORM and then create simple Entities that more closely resemble a proper database structure. Lazy loading could be implemented in the repository (I think you call it DAO). True, this lazy loading implementation would only work if the property returns an interface or is virtual... – Daniel Hilgarth Oct 07 '11 at 19:11
  • Furthermore: Making members `virtual` is something I could live with, but other compromises not (for example default constructors) – Daniel Hilgarth Oct 07 '11 at 19:17
  • I would only add that the database doesn't need to be that similar. Data modeling is very mature and the best design for data in a system can be modeled in a form appropriate to be implemented in a RDBMS independent of the existence of a system to manipulate it. It should be enlightened by the overall system, but should be able to be refactored for database needs without affecting other parts of the system. Thus the importance of ORM systems - mapping between two quite different systems which are each optimal for their own domains. – Cade Roux Oct 09 '11 at 16:41
  • @Daniel - There is no problem that cannot be solved by adding another layer of indirection, except for having too many layers of indirection. That's a perfectly workable solution, but it adds the layer of DTOs that the ORM will hydrate, plus a layer to transform the DTOs into hydrated domain classes. And, like I said, you lose a lot of the querying power of an ORM if you aren't working directly with the objects the ORM uses; you can't use queryables, and the conversion process basically forces eager-loading of the full graph for a top-level object. – KeithS Oct 18 '11 at 21:20
  • 1
    @KeithS: After experimenting with this and using NHibernate in two complex real world applications, I came to agree with your point of view. Thanks for taking your time and sorry for not appreciating your answer back than. Please see [my answer](http://stackoverflow.com/a/14376163/572644) for a more concrete overview on how I solved those problems for me. – Daniel Hilgarth Jan 17 '13 at 09:50
3

All good points.

I don't have an answer (but the comment got too long when I decided to add something about stored procs) except to say my philosophy seems to be identical to yours and I code or code generate.

Things like partial classes make this a lot easier than it used to be in the early .NET days. But ORMs (as a distinct "thing" as opposed to something that just gets done in getting to and from the database) still require a LOT of compromises and they are, frankly, too leaky of an abstraction for me. And I'm not big on having a lot of dupe classes because my designs tend to have a very long life and change a lot over the years (decades, even).

As far as the database side, stored procs are a necessity in my view. I know that ORMs support them, but the tendency is not to do so by most ORM users and that is a huge negative for me - because they talk about a best practice and then they couple to a table-based design even if it is created from a code-first model. Seems to me they should look at an object datastore if they don't want to use a relational database in a way which utilizes its strengths. I believe in Code AND Database first - i.e. model the database and the object model simultaneously back and forth and then work inwards from both ends. I'm going to lay it out right here:

If you let your developers code ORM against your tables, your app is going to have problems being able to live for years. Tables need to change. More and more people are going to want to knock up against those entities, and now they all are using an ORM generated from tables. And you are going to want to refactor your tables over time. In addition, only stored procedures are going to give you any kind of usable role-based manageability without dealing with every tabl on a per-column GRANT basis - which is super-painful. If you program well in OO, you have to understand the benefits of controlled coupling. That's all stored procedures are - USE THEM so your database has a well-defined interface. Or don't use a relational database if you just want a "dumb" datastore.

Cade Roux
  • 88,164
  • 40
  • 182
  • 265
  • 1
    This answer exactly expresses my concerns. I also like to design both, the ORM and the database, "in isolation", i.e. without making compromises because of the other part of the mapping... – Daniel Hilgarth Oct 07 '11 at 17:51
  • Working on quite heavy project for more than a year now. Apart from small upgrade scripts, it has zero sql lines of code and even less stored procedures. Try focusing on user requirements instead of foreign keys - code "resolves by itself" then. – Arnis Lapsa Oct 07 '11 at 17:51
  • Belief that "heart of the system" lives in database is misleading and harmful. It's only our knowledge about business domain and solutions of its problems that matters. – Arnis Lapsa Oct 07 '11 at 17:55
  • IMO, heart of the system should be its domain model – np-hard Oct 07 '11 at 18:01
  • "only stored procedures are going to give you any kind of usable role-based manageability without dealing with every tabl on a per-column GRANT basis - which is super-painful" -> http://stackoverflow.com/questions/3964989/how-to-pass-current-user-information-to-all-layers-in-ddd/3969014#3969014 ...scroll down to see actual usage. It's just calling `Authorize()`. Nothing painful about that. – Arnis Lapsa Oct 07 '11 at 18:01
  • @np_hard domain model is derived from this understanding. while it is most valuable code you will have, it has no value itself without problems we are trying to solve. – Arnis Lapsa Oct 07 '11 at 18:05
  • 1
    @Arnis L. That's client code. The database has to protect its perimeter - you need to be able to audit the changes in the database and know where they are coming from. If your database does not protect its perimeter, you have to question trusting its data. A system may have a "heart" but a system is a set of interconnected components. The database is one just like any other component in OO, and they protect their perimeters too. What's special about ORM-based systems that they prefer to leave their data in a naked dumb datastore? – Cade Roux Oct 07 '11 at 21:14
  • @CadeRoux what I'm saying is that you should at least recognize that there are multiple ways how you can develop your system that solves problems efficiently and reliably not just by using and abusing relational databases. there is nothing special - it's technology+some conventions either way. something unique and special is in what your client tells to you. – Arnis Lapsa Oct 09 '11 at 12:18
  • 1
    @Arnis L. What I'm saying is that using ORM with a dumb datastore is an oxymoron. Why would you want to map from a great OO model to a mediocre data model which does not utilize many/any strengths of relational databases? Why map? The vast majority of ORM devotees do not concentrate on their database. They concentrate only on the OO model - thereby diminishing the value of the R and M parts. They are paying lip-service to the value of databases to be able to say - we're using industry-standard Oracle or SQL Server databases, without admitting that they aren't using that investment wisely. – Cade Roux Oct 09 '11 at 14:18
  • @CadeRoux I'm using sqlite. anyway - You are still thinking and talking about technology only... – Arnis Lapsa Oct 09 '11 at 16:23
  • @Arnis L. I'm not following. Are you saying OO is "closer" to the non-technology aspects of a system design? I would argue that it depends, and that when you look at complex systems, if you've chosen a RDBMS system for a reason, you are likely going to have multiple components accessing the RDBMS and because of this, you will want the database to have a robust and well-defined perimeter of low-level services in order to reduce coupling and provide value to the business for their database investment. I would also argue that databases will long outlive applications and app paradigms. – Cade Roux Oct 09 '11 at 16:37
  • @CadeRoux nope. I'm saying that technology does not matter much. – Arnis Lapsa Oct 09 '11 at 20:09
  • i work on a production app which uses sprocs. It's horrible to maintain them. Each time you want to add 1 column you have to rewrite 4+ sprocs (CUD and 1+ R) make a script to add the column and change several classes which access the different sprocs to support the changed parameters and resultsets. Compare that to add property + (add mapping if not automap) + run schemaupdate to get updatescript -> 2 lines of code + copypaste updatescript. – Firo Jan 28 '12 at 22:58
  • @Firo A procs layer is an abstraction layer like any other in a system. People find them valuable in OO systems; they attack them in database systems. Does the database have to have a secure and easily auditable perimeter interface or would granting table access be acceptable (secured modifiable views with triggers is just as much work as procs)? Is all db access through a controlled application level library and all the various client applications get updated at the same time? Depending upon the answers to this question, an ORM with an open database might just be fine for you. – Cade Roux Jan 28 '12 at 23:42
0

Over a year later, I have solved these problems for me now.

Using NHibernate, I am able to map fairly complex Domain Models to reasonable database designs that wouldn't make a DBA cringe.

Sometimes it is needed to create a new implementation of the IUserType interface so that NHibernate can correctly persist a custom type. Thanks to NHibernates extensible nature, that is no big deal.

I found no way to avoid adding virtual to my properties without loosing lazy loading. I still don't particularly like it, especially because of all the warnings from Code Analysis about virtual properties without derived classes overriding them, but out of pragmatism, I can now live with it.

For the default constructor I also found a solution I can live with. I add the constructors I need as public constructors and I add an obsolete protected constructor for NHibernate to use:

[Obsolete("This constructor exists because of NHibernate. Do not use.")]
protected DataExportForeignKey()
{
}
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
0

Have you looked at the Entity Framework 4.1 Code First? IIRC, the domain objects are pure POCOs.

Jason Miesionczek
  • 14,268
  • 17
  • 76
  • 108
  • 1
    NHibernate supports code first as well. That's not the problem. The problem is that I think it is more or less impossible to map a reasonably complex domain model to a relational database. – Daniel Hilgarth Oct 07 '11 at 17:33
  • @DanielHilgarth and You are right about that... on the other hand - you will never be able to express mental model w/o any loss. so - just accept it. it's more about knowing thoroughly trade-offs you are making. – Arnis Lapsa Oct 07 '11 at 17:36
0

this what we did on our latest project, and it worked out pretty well

  1. use EF 4.1 with virtual keywords for our business objects and have our own custom implementation of T4 template. Wrapping the ObjectContext behind an interface for repository style dataaccess.
  2. using automapper to convert between Bo To DTO
  3. using autoMapper to convert between ViewModel and DTO.

you would think that viewmodel and Dto and Business objects are same thing, and they might look same, but they have a very clear seperation in terms of concerns. View Models are more about UI screen, DTO is more about the task you are accomplishing, and Business objects primarily concerned about the domain

There are some comprimises along the way, but if you want EF, then the benfits outweigh things that you give up

np-hard
  • 5,725
  • 6
  • 52
  • 76