views:

737

answers:

7

Persistence ignorance is typically defined as the ability to persist & retrieve standard .NET objects (or POCOs if you really insist on giving them a name). And a seemingly well accepted definition of a standard .NET object is:

"...ordinary classes where you focus on the business problem at hand without adding stuff for infrastructure-related reasons..."

However, I see people describing NHibernate as a framework that allows persistence ignorance, and yet it is a framework that cannot work on any standard .NET object, only standard .NET objects that adhere to particular design requirements, for example (source):

  • All classes must have a default constructor
  • Some features don't work unless classes are unsealed and all members are virtual
  • Object identity doesn't work properly unless you abuse Equals/GetHashCode

(Aside: Before anybody gets upset, I don't mean to pick on NHibernate here, it's just a frequently quoted example of a framework that supposedly permits persistence ignorance. I'm sure similar arguments could be applied to other ORMs that claim the same.)

Now although the class in itself does not have any persistence-framework-specific attributes or base classes etc., to me it is not really "persistence ignorant" because it must follow a set of design guidelines to facilitate use by the chosen persistence framework. You must design and implement the class with the requirements of the persistence framework in mind; if you are ignorant of it the class may not work with it.

Where I'm having trouble with the definition of "persistence ignorance"/"POCO" is that I don't see how, conceptually, this is really any different to adding attributes such as [Serializable] or [DataContract] or [XmlType] or any other persistence-framework-specific annotations that facilitate the persistence and retrieval of the entity using that framework.

So, what exactly is "persistence ignorance"?

Clearly the definition of it as being able to persist "ordinary classes" is a fallacy because the NHibernate ones are only ordinary insofar as not referencing framework-specific classes, whereas they are extraordinary inasmuch as they require unusual design choices such as default constructors and all-virtual members and Equals/GetHashCode implementations on mutable types.

Is it therefore reasonable to say that "persistence ignorance" is true when objects facilitate the use of a persistence framework (either in design and structure or by use of framework-specific annotations) but do not perform any persistence logic themselves?

+1  A: 

I'd agree with your definition:

Is it therefore reasonable to say that "persistence ignorance" is true when objects facilitate the use of a persistence framework, but do not perform any persistence logic themselves?

The code (as opposed to atributes) in your classes has no features that are intrinsic to persistence. Default constructors might be needed for persistence, but have no code that actually does persistence. The persistence layer could be changed quite substantially, different databases could be used and the business logic would remain unchanged.

djna
@djna - I've updated the my definition to make it more clear that I believe classes decorated with framework-specific annotations would still be considered persistence-ignorant, as I realised reading your response that it may be unclear. It sounds like you would still agree with that definition though as you're considering only the executable code, not metadata?
Greg Beech
I do agree, assuming that we can run the code without the metadata "interfering".
djna
A: 

While there may be certain minor constraints that any given persistence-ignorance framework requires, persistence-ignorance nevertheless remains in place.

While a class in your domain model (transparently persisted with NHibernate) must have a no-arguments constructor so that it can be constructed "dynamically," it is not required to have a certain base class dictated by the framework nor is it required to have or override certain framework-specified methods.

Justice
@Justice - It seems like you've just re-stated my question. How do you see restrictions in the structure and design of an object to facilitate the use of a framework as being conceptually different to using annotations to facilitate the use of a framework?
Greg Beech
The constraints imposed by persistence-ignorant ORMs like NHibernate amount to little more than the constraints imposed by serialization plus aspect-oriented programming (where we treat persistence as just another aspect that uses serialization).
Justice
@Justice - I'm not clear whether you are saying that is conceptually different to, say, runtime serialization requiring a `[Serializable]` attribute or data contract serialization requiring a `[DataContract]` attribute? It's similarly a constraint that facilitates a type of serialization framework.
Greg Beech
Neither `[Serializable]` nor `[DataContract]` makes it possible for a generic serialization mechanism to work. Those are particular classes from particular framework. Classes that have no-argument constructors because serialization *at all* needs no-argument constructors can still be serialization-ignorant. But classes that have `[Serializable]` attributes are no longer serialization-ignorant, nor are classes implementing `ISerializable`.
Justice
@Justice - `[Serializable]` exactly facilitates generic serialization mechanisms; it is nothing more than a convention to indicate that you are permitting the serialization of the class. You'll note the .NET framework contains disparate formatter implementations that use this attribute (two binary and one XML). To me it is less of a change to the behaviour of the class than adding a constructor because it is metadata, not executable code.
Greg Beech
(Also, serialization *at all* does not need a no-argument constructor. You can call FormatterServices.GetUninitializedObject from a fully trusted assembly, which is exactly what the IFormatter classes do. Therefore having a default constructor for serialization purposes only is at least on some level an acknowledgement of the presence of some type of serialization that requires it, because not all do, and thus the class cannot be said to be truly serialization ignorant, IMHO.)
Greg Beech
+5  A: 

I would claim that, like most things, its a sliding scale. There are things that we make that want to have the property of persistence. On one end of the scale is this thing having all of the guts, dependencies, and code that is custom built to persist just this one thing in its particular way. On the other end of the scale is something that just magically happens, all without us doing much more than adding a token or setting a property somewhere that causes that thing to 'just persist'. In order to get to the magical side of the scale, there are frameworks, design guidelines, conventions, etc that assist the magic in happening. I think you could argue that a tool could be produced that had fewer requirements and restrictions than NHibernate but pursued the same goal; that hypothetical tool would be further along our scale.

I don't know that I like the term 'persistence ignorance' so much; its really about an object being ignorant of the implementation, the backing store, the cache, that sort of thing - an object is typically aware of whether or not it is persistent, though. But that's just semantics.

Mikeb
A: 

A persistant ignorant class, is a class that is not tied to a persistancy framework.

That is, the class has absolutely no knowledge that there's a persistancy framework present, it does not inherit from a class that is defined by that framework nor does it implement an interface that is required for that persistance framework in order to work.

Frederik Gheysels
@Frederik - So by that definition you are saying that NHibernate does not support persistence ignorance? As if the class does not need a default constructor for normal use, it must still have one purely due to knowledge of the requirements of the persistence framework?
Greg Beech
Well NHib also requires that properties etc are declared as virtual. But I wouldn't say this affects ignorance really, as the requirements are very minor and do not affect your design at all.
UpTheCreek
@Greg: no, I'm not saying that. I'm saying that persistance ignorance is , if your class does not require to have a 'reference' to that persistency framework.Indeed, the fact that you need a default constructor (which can be private), is a glitch, but that doesn't mean that it is not persistance ignorant.
Frederik Gheysels
@Sosh: wrong. NH does not require that properties are declared as virtual. I use NH every day, and I hate the fact that I need virtual properties if the semantics do not require that, so, I only create virtual properties if necessary. When you specify that NH should not use dynamic proxies, you do not require virtual props.
Frederik Gheysels
@Frederik - So would I be right in saying that you believe a class is persistence ignorant if it does not have a 'reference' to a specific persistence framework, even if it has significant design changes effected by the need to work with a specific framework (e.g. mandatory default constructor, virtual members for lazy loading, subverted Equals/GetHashCode for identity equality)? What if another framework required a different use of Equals/GetHashCode (e.g. for all-member equality) so your object could not work with it as it is written for NHibernate; is that still persistence ignorant?
Greg Beech
For me, NH is persistence ignorant, since NH doesn't require your members to be virtual (you just have to live with the 'disadvantage' of not having dynamic proxies, but I value semantics higher then the performance gain you get with dyn. proxies).Next to that, the mandatory default constructor is indeed an argument that you can use that NH is not PI, but, you can work around it by providing a private (or protected) default constructor.Compared with what other persitency frameworks require, I think that NH is quite persistency ignorant yes.
Frederik Gheysels
And, concering the Equals/GetHashCode implementation:this is something that has nothing to do with the persitency framework IMHO. You should implement the equals/gethashcode methods with the semantics that you want, in the back of your mind. What I mean is: if you have an entity, the equals method will be implemented in a different way than if you have a value-object. (With value objects, you'll implement 'Equals' as 'all-member-equality').(I use NH, and some of my classes that I persist using NH, have 'all-member-equality' implemented in the Equals method).
Frederik Gheysels
A: 

In my opinion, "persistence ignorance" is a property of your model (domain model, business model or whatever you might refer to it as). The model is persistence ignorant because it retrieves instances of the entities it contains through abstractions (sometimes referred to as repositories). These abstractions can be implemened by using an ORM directly, but as you state yourself this may sometimes add requirements to the objects that do not naturally belong in your model. Therefore I would not say that a model that adheres to some requirements of a specific ORM is 100% persistence ignorant.

klausbyskov
I do not agree entirely.You can have a domain class that is retrieved via an abstraction like a repository, but that is not PI. It is possible that the persistance framework you use for instance, requires that your domain class inherits from some base-class that has been defined by the persistance-framework.
Frederik Gheysels
+1  A: 

I don't believe your understanding (or definition) of "Persistence Ingorance" is wrong.

The real issue is that of leaky abstractions. Quite simply, the existing technology makes it very difficult to implement true PI.

Vijay Patel
+2  A: 

I agree with Mikeb - "persistance ignorance" is a sliding scale, not a true/false property of a given ORM.

My definition of true 100% PI would be that you could persist ANY possible POCO class, no matter how convoluted and linked to other classes, without otherwise changing the class in any way.

Adding ID fields, decorating with attributes, inheriting from ORM classes, having to design your classes so they map well to the underlying tables in an RDB - all reduce the "PI score" below 100%.

This said, I've chosen to use Fluent NHibernate Automapping because it seems to have the highest PI score of any of the ORM options I've looked at.

Tom Bushell
+1 agreed - there's no 100% PI solution out there, so you need to ask yourself the questions Jeremy Miller poses in his article (cited in the question): "Can the Business Logic Run Independently of the Database?", "Can I Design My Domain Model Independently from the Database Model?", and "How Does My Persistence Strategy Affect My Business Logic?"
Jeff Sternal