Boost-enabled loosely coupled ORM serialization

Hello, I am coming up on a decision in the architecture I am working on. I want to serialize some domain data to a data store. I'm aware boost has serialization features, which I'd like to explore further. I'm coming at this first from a boost standpoint because I am successfully building upon it for other parts of the engine and domain model, so boost is available right now. I am expecting I might go with a third-party library like ODB, Wt::Dbo, or something like that. Also, background, I am no stranger to ORM concepts. I was first exposed to them through Java, C# .NET, and have dealt with Hibernate, NHibernate, Fluent NHibernate, and so on. So vocabulary-wise, grammar-wise, we're talking the same language. Also, I'll defer backend decisions until later. Probably that will be somewhat determined by target platform support and so on: landing on ArchLinux ARM. For now, my focus for now is on keeping ORM intersection with my domain as loosely coupled as possible. For anyone versed in ORM nuances, for obvious reasons, I want for the ORM to be as transparent through the domain model as possible. I want to steer clear of the active record anti-pattern whenever possible. I'd like to gear up for a repository pattern whenever feasible. No time to get into that discussion why I believe these are patterns or anti-patterns. That's a separate blog for another time. Wrappers like smart pointers, smart collections, are understandable, and may be the way to go, but put opaqueness layers between end-users of the domain objects and the rest of the model. Will use them if I must, but I'd like to avoid them if possible. Additionally, given some language "features", perhaps "limitations" if you will, of C++11, as contrasted with a C# let's say, I also want for any ORM code, whether meta-programmed or not, to be as non-intrusive to the domain model as possible. I've seen a ton of code that have persist methods, friend access, macros, and such. Sounds like a tall order I know. Call me spoiled on the "niceness" of .NET, Reflection, and so on. More likely I'm getting reacquainted with the practicalities of using a paradigm-rich language like C++11. Now back to the "real world" what the rest of us deal with. Realistically, a sober look at it, I believe there will be tradeoffs: SOLID is my goal, sufficiently extensible to meet the seams we'll need to meet, yet maintainable at the end of the day. Regards, Michael Powell

On Fri, Apr 5, 2013 at 11:19 AM, Michael Powell
Sounds like a tall order I know. Call me spoiled on the "niceness" of .NET, Reflection, and so on. More likely I'm getting reacquainted with the practicalities of using a paradigm-rich language like C++11. Now back to the "real world" what the rest of us deal with.
Indeed. I'm facing similar issues. I've introduced ad-hoc introspection, but doing the reflection part right, in a typesafe manner, which plays nice with aggregate C++ types which expand into several SQL columns (with C++ to SQL type transformations in the middle), or which play nice with polymorphic foreign-keys (which can also expand to several columns if modeled using XARC), is currently out-of-reach for us, worked-around by lots of ad-hoc code again. The introspection part at least helps with SQL schema management and statement SQL text generation, but without reflection of lot must be spelled out in explicit code. I'm aware of some high-voodoo template-based introspection libraries in the boost sandbox or announced on the list, but haven't used them, as they tend to be too "templat'y" to be accepted in my organization. I don't know if any actually provides reflection. Adding introspection and reflection as a library feature tend to yield complex and slow to compile code it seems, compared to what a language feature could provide, and almost always involves lots of macros too. But I'm getting off track here. Hopefully this thread can provides interesting nuggets of wisdom on this subject. --DD

Zitat von Michael Powell
Additionally, given some language "features", perhaps "limitations" if you will, of C++11, as contrasted with a C# let's say, I also want for any ORM code, whether meta-programmed or not, to be as non-intrusive to the domain model as possible. I've seen a ton of code that have persist methods, friend access, macros, and such.
Sounds like a tall order I know. Call me spoiled on the "niceness" of .NET, Reflection, and so on.
while there is no out-of-the-box solution for ORM currently, I do think that Boost.Serialization provides the reflection necessary to build one. the proposed introspection libraries that have been referred to as "too templat'y" try to provide introspection on the entire C++ object model. for ORM, especially for losely-coupled ORM that doesn't automatically create a database schema, you only need reflection of data fields, their types, class inheritance structure and the like, which the Serializable concept of Boost.Serialization does provide [1]. also see the use of name-value-pairs, which can refer to database fields in ORM [2]. Building a Serialization Archive that collects all the fields, field names and types, including aggregate types, so you can iterate through them dynamically as you might be used to from .NET, should be easy enough. However, for a really generic solution to C++ ORM there are a few more problems to solve, that Boost.Serialization also had to solve, but geared towards an unformatted data stream. Among them: - optional serialization: a serialize() function can always leave out fields or introduce new ones, so you can never be sure that your database schema is complete - "pointer tracking"/foreign keys: Serialization maps pointers to object IDs. for ORM pointers have to be mapped to table-specific foreign keys. as an intermediate solution you could use a serialization wrapper (see [3]) to provide the necessary info to the Archive when serializing a pointer. - dynamic types/polymorphic foreign keys: Serialization uses user-defined strings to refer to dynamic object types (see [4]), for ORM you'll have to come up with a solution to refer to database tables of the most-derived type. - containers/non-unique names in NVPs: serialize() functions are allowed to put out multiple name-value-pairs with the same name. e.g. std::vector creates an "item" NVP for each element. your ORM Archive has to detect that and map it to a M:N relation. There might be more. I've thought about this for a while because I'm the author of a proposed boost library for object persistence. No ORM though at this point, currently it dumps unformatted serialized data into the database backend. HPH a bit, I'd be interested in any solution you come up with. [1] http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/serialization.ht... [2] http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/wrappers.html#nv... [3] http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/wrappers.html [4] http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/special.html#exp...
participants (3)
-
Dominique Devienne
-
Michael Powell
-
Stefan Strasser