Hanalei, Hawaii 9/2/2010
438 Posts and Counting

Crazy Talk: Reducing ORM Friction

Monday, November 03, 2008 -

Let's get this out of the way: I know you're going to think I'm nuts as you read this. You may "pfft" to what you're about to read - know that I know you're "pfft"-ing me. All I ask is that you consider what I'm about to suggest...

Development Frictionorm-d900
The term "friction" gets thrown around a lot in terms of development. It's what it sounds like: something you do or a process you undertake that slows you down as you crank out code. If you think on this for a second - when you're building an application what's the number one thing that slows you down (technically speaking. Running out of Red Bull doesn't count)?

For me it's the database "stuff". It's why I made SubSonic - I was tired of thinking about it and I wanted something faster and easier.

Tangent
This whole post (and thing I'm about to dive into) was dreamt up during a bike ride to the store. I thought a fun post idea was to "send a post back in time" and entitle it "Greetings from the Year 2012", and I would laugh at all the silly stuff I did in 2008. The top of that list, for me, is the continuing struggle we have with persisting data "properly". After 10+ years, the marriage of web and database is still arguing over the same old stuff. Can't we move on? Shouldn't this be easier by now?

You might have other sources of friction, or you might be saying "dude [MY ORM] roolz! LOLZ at U". I'll bet it does - but you still need to work with it (and your database) as you build out your site. Even if it takes you 1 minute to "update and regen" - you still have to mess with the DB and mess with mapping (if you do that).

I've talked with a lot of people over the last few weeks about this and asked them the same question:

In 5 years what do you think will finally be changed?

The answer, every single time, is a variation on "ORM's will finally work properly". What if I told you that you don't need to wait for 5 years for this? What if I told you to ditch your ORM and your database and focus on what's the most important thing: your application?

How Do You Do What You Do?
There are generally two camps of developers:

  1. The guys who create a database and then build the app, and then update the database, and then change the app, etc.
  2. The guys who write tests, create a model, update their database and remap their ORM, rinse and repeat.

If you're a TDD fan (or want to be) you might be interested in Domain-Driven Development (DDD). Yes, it's another one of those buzz-words but you might actually be a DDD person right now, and not even know it. Check it out:

"What it's all about is creating as simple a model as possible, one that still captures what's important for the domain of the application. During development the process could really be described as knowledge-crunching by the developers and domain experts together. The knowledge that is gained is put into the model."
(Nilsson, Applying Domain-driven Design and Patterns)

skull_of_ormYes, I'm quoting a DDD book. I've been absorbed :). The point here is that most of us have always worked this way - working closely with clients to understand their business, and making sure what we build is focused.

The split comes in with what system you work from: the database or the tests? Which is more appropriate in terms of building out an application domain? I'm hoping to convince you, today, to toss your database as you don't need it. Not yet anyway. Focus on your tests and your domain and I think you'll see that you can move a lot faster.

Database YAGNI (or the Database as a Feature)
YAGNI is the principle of "You Aint Gonna Need It", which essentially means "don't add it unless requirements/testing make you add it" and is one of the main drivers of TDD. It keeps code light and manageable, and keeps cruft out of your application.

I think this should apply to architectures as well - why implement, design, and build a database (with data access code) when the application doesn't need it? And yes, this is where the Crazy Talk kicks in.

<CrazyTalk>

Databases are pretty heavy information organization and retrieval systems. Even the lighter-weight ones are capable of powering some pretty high-level querying at very rapid speeds.

Do you need this when you're developing? Do you need to play "ORM-Catch up"? Probably not.

What if, in 10 years, the platform could just translate your model for you and save it properly without you thinking about it?

What if I told you that you can do that now? Well you can - and this is where you're gonna say "PFFT" and tell me I'm crazy. But as I said - I know you're going to say it... so just pretend I can hear you...

</CrazyTalk>

Off The Reservation with OODBs
Object Databases have been around for a long, long time. If you've never heard of these things, well they basically crumple your object up into binary (by serialization) and save it to disk for you to access later. If you'd like to read more, this is a great post on OODBs, what they are, and how they work. To summarize:

An OODBMS is the result of combining object oriented programming principles with database management principles. Object oriented programming concepts such as encapsulation, polymorphism and inheritance are enforced as well as database management concepts such as the ACID properties (Atomicity, Consistency, Isolation and Durability) which lead to system integrity, support for an ad hoc query language and secondary storage management systems which allow for managing very large amounts of data. The Object Oriented Database Manifesto [Atk 89] specifically lists the following features as mandatory for a system to support before it can be called an OODBMS; Complex objects, Object identity, Encapsulation , Types and Classes ,Class or Type Hierarchies, Overriding,overloading and late binding,Computational completeness , Extensibility, Persistence , Secondary storage management, Concurrency, Recovery and an Ad Hoc Query Facility.

Here's my thought for you: What if you used an OODB for development ONLY and implemented SQL Server later, when you know what you need to create.

You can't ditch your RDBMS entirely - never. However I will suggest that working on two systems at once, when you don't need to, is silly. You can do a lot more in terms of RAD development right now - and port to SQL later.

Doesn't it make more sense to create the database when, and only when, you need it? Hold that thought - I'm coming back to it.

DB4O - A Free OSS Object Database
I've been using DB4O a lot over the last few months and I really like it. The tutorials are very easy and getting up to speed is no problem at all. First, however, I'd like to go over some things you might be wondering about.

Let's say I have three objects in my model:

    public class Product {
        public string Name { get; set; }
        public Supplier Supplier { get; set; }
        public IList<Review> Reviews { get; set; }
    }

    public class Review {
        public string AuthorEmail { get; set; }
        public string Body { get; set; }
    }

    public class Supplier {
        public string Name { get; set; }

    }

You might be wondering about how these objects are persisted, and how integrity might be enforced. It's actually required that they enforce many of the same concepts as an RDBMS so that you don't make a mess out of your model storage.

For instance, if I create a Supplier for a Product, it will be stored as an independent Supplier that I can then assign to another Product. If I change that Supplier's name, it will get changed for both. It's a single object, and the OODB works with the idea of "pointers" in the same way that a regular database does. The thing here, however, is that there is no joining - the relationships implicit and understood. In this way, an OODB can actually (and often do) outperform their RDBMS counterparts.

But I'm not here to talk about perf and scaling - it doesn't matter for what I'm suggesting :).

If you're curious and want to play along at home, go and download DB4O from their website and install it. To use it you have to make a few references in your application (specifically Db4objects.Db4o.dll). You then need to write some wrapper code - but I've got that covered for you:

using Db4objects.Db4o;
using System.Web;
using System.IO;

public class DB4O {

    static readonly object padlock = new object();

    // static object container variable 
    static IObjectContainer _db = null;

    static string _dbPath = System.Configuration.ConfigurationManager.ConnectionStrings["ObjectStore"].ConnectionString;
    public static string DBPath {
        get {
            return _dbPath;
        }
        set {
            _dbPath = value;
        }
    }

    public static IObjectContainer Container {
        get {
            lock (padlock) {
                if (_db == null) {

                    //check to see if this is pointing to data directory
                    //change as you need btw
                    if (_dbPath.Contains("|DataDirectory|")) {

                        //we know, then, that this is a web project
                        //and HttpContext is hopefully not null...

                        _dbPath = _dbPath.Replace("|DataDirectory|", "");
                        string appDir = HttpContext.Current.Server.MapPath("~/App_Data/");
                        _dbPath = Path.Combine(appDir, _dbPath);
                    }

                    _db = Db4oFactory.OpenFile(_dbPath);
                }
                return _db;
            }
        }
    }

    public static void CloseContainer() {
        if (_db != null) {
            _db.Close();
        }
        _db = null;
    }

 

This code assumes that you

Have an App_Data directory and that you have a connection string in your Web.config like this one:

<add name="ObjectStore" connectionString="|DataDirectory|ObjectStore.yap"/> 

Yes, that's a singleton you see there, and yes I know it's probably making you cringe. The reason we need to use a singleton is that the IObjectContainer locks the binary file where the data is kept. File locking and singletons might not sit well with you right now, but in this case is that the way I have this setup here is for a single user - ME - because I'm developing against it. If this were a live app I would be able to set a bunch of settings to make this thread-safe etc.

But it's not production - it's development only (have I mentioned this yet?) so you don't need to worry about singletons, perf, and scaling.

Great, so now that we have our container, let's store an object. This isn't really a test, but you've read a lot more crazy stuff so far, so this won't surprise you much. Consider this a spike please - or maybe pretend I'm asserting something:

[TestMethod]
public void ObjectRepo_Should_Store_Product() {
    Product p = new Product();
    p.Name = "test product";
    p.Supplier=new Supplier("Test supplier");
    DB.Container.Store(p);
}

Yes, it's that easy. But that's not the best part. I can add a DLL to the project that DB4O just released, called "Db4objects.Db4o.Linq" and it does what you might imagine, which is exremely cool:

[TestMethod]
public void ObjectRepo_Should_Return_Product() {
    var result = from Product p in DB.Container
                 where p.Name == "test product"
                 select p;

    Assert.AreEqual(1, result.Count());
}

Yes, that would be LINQ, working with an OODB container. We can also query a bit deeper, with no problems:

[TestMethod]
public void ObjectRepo_Should_Return_Product_By_Supplier() {
    var result = from Product p in DB.Container
                 where p.Supplier.Name == "Test supplier"
                 select p;

    Assert.AreEqual(1, result.Count());
}

And to illustrate my point above about object independence and integrity, I can also get the Supplier, independent of the Product:

[TestMethod]
public void ObjectRepo_Should_Return_Supplier() {
    var result = from Supplier s in DB.Container
                 select s;

    Assert.AreEqual(1, result.Count());
}

This, literally, is the tip of the iceberg. DB4O has support for transactions, indexing, and many different ways of querying to improve performance and usage. But I'm not talking about perf here :) - it doesn't matter, this is only for development.

Also it's worth noting that if I change the properties of Product around, it won't break. The changed property just won't get loaded - but everything else will. So you can change and alter as required and nothing breaks!

Real-World Application
What I'm suggesting is that you can create a IRepository<T> and then implement a nice ObjectRepository<T> to work with in your application. It's very simple - and yes, here's some more code for you:

using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;

public interface IRepository<T> {

    IQueryable<T> GetAll();
    PagedList<T> GetPaged(int pageIndex, int pageSize);
    IQueryable<T> Find(Expression<Func<T, bool>> expression);
    void Save(T item);
    void Delete(T item);

}

Now, you can implement this quite nicely with DB4O:

using System;
using System.Collections;
using System.Linq;
using Db4objects.Db4o.Linq;


public class ObjectRepository<T> : IRepository<T> where T: class {

    /// <summary>
    /// Returns all T records in the repository
    /// </summary>
    public IQueryable<T> GetAll() {
        return (from T items in DB4O.Container
                select items).AsQueryable();
    }

    /// <summary>
    /// Returns a PagedList of items
    /// </summary>
    /// <param name="pageIndex">zero-based index to be used for lookup</param>
    /// <param name="pageSize">the size of the paged items</param>
    /// <returns></returns>
    public PagedList<T> GetPaged(int pageIndex, int pageSize) {
        var query=(from T items in DB4O.Container
                select items).AsQueryable();
        return new PagedList<T>(query,pageIndex,pageSize);        }

    /// <summary>
    /// Finds an item using a passed-in expression lambda
    /// </summary>
    public IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> expression) {
         return GetAll().Where(expression);
    }

    /// <summary>
    /// Saves an item to the database
    /// </summary>
    /// <param name="item"></param>
    public void Save(T item) {
        DB4O.Container.Store(item);
    }

    /// <summary>
    /// Deletes an item from the database
    /// </summary>
    /// <param name="item"></param>
    public void Delete(T item) {
        DB4O.Container.Delete(item);
    }

}

If you're wondering what a "PagedList" is - you can find out more here.

ORM Selection Made Easy
Suppose you didn't need to worry about your database as you build your application. Better yet, suppose you didn't need to worry about your ORM! This latter thought is actually a critical, critical item. When I suggested that you could wait until you're about to launch to actually implement a database, a friend of mine asked me "well if you do that, you might back yourself into such a complex model that your ORM won't handle it. What do you do then?".

And I said "BINGO".

Put another way, what you're getting by not worrying about your ORM (until you need it) is the freedom to develop your app without influence from your database. It is true that you can make a model that's too complex for your favorite ORM - but doesn't that mean your favorite ORM probably wasn't up to the task anyway? Isn't it much nicer to find that out the easy way?

In most cases you can probably just jump over to SQL very simply, just by replacing the reference to ObjectRepository to something like SqlRepository (this code is using Linq To Sql - but you can change this out with EF in the future  - I'll try to update this later):

using System;
using System.Collections;
using System.Data.Linq;
using System.Linq;

public class SqlRepository<T> : IRepository<T> where T: class {

    NorthwindDB.DB _db = null;
    public SqlRepository(){
        _db=new NorthwindDB.DB();
    }
    /// <summary>
    /// Gets the table provided by the type T and returns for querying
    /// </summary>
    private Table<T> Table {
        get { return _db.GetTable<T>(); }
    }

    /// <summary>
    /// Returns all T records in the repository
    /// </summary>
    public IQueryable<T> GetAll() {
        return Table;
    }

    /// <summary>
    /// Returns a PagedList of items
    /// </summary>
    /// <param name="pageIndex">zero-based index to be used for lookup</param>
    /// <param name="pageSize">the size of the paged items</param>
    /// <returns></returns>
    public PagedList<T> GetPaged(int pageIndex, int pageSize) {
        return new PagedList<T>(Table, pageIndex, pageSize);           
    }

    /// <summary>
    /// Finds an item using a passed-in expression lambda
    /// </summary>
    public IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> expression) {
        return Table.Where(expression);
    }

    /// <summary>
    /// Saves an item to the database
    /// </summary>
    /// <param name="item"></param>
    public void Save(T item) {

        if (!Table.Contains(item)) {
            Table.InsertOnSubmit(item);
        }
        _db.SubmitChanges();
    }

    /// <summary>
    /// Deletes an item from the database
    /// </summary>
    /// <param name="item"></param>
    public void Delete(T item) {
        Table.DeleteOnSubmit(item);
        _db.SubmitChanges();
    }

}

Since we're wrapping everything in IRepository<T>, you can swap parts as needed. I'll be you were wondering why you might want to develop this way (I know I was a long time ago - who really ever swaps components anyway?) - but this is a good example of why you might want to decouple your system as much as possible.

Swapping out data stores like this is equivalent to turning your minivan into a ferrari if you want to drive the Autobahn, and back again when you need to get the kids from Soccer.

Am I nuts? If you think I am - please give me some details as I think this would make for an interesting discussion. Just don't tell the Alt.NET guys yet :).

Related


Gravatar
tobinharris - Sunday, November 16, 2008 - Great post. I think you've just drummed up quite a lot of interest in DB40 too. Like many other folks here, I've been "watching" it for years but never had a stab at using it. Cheers for the helper class too!...



As for the idea. I like the fact that it reduces friction up-front.



I don't like the fact that it creates a debt to be paid off downstream. That is, when the time comes to switch to RDBMS. Or, if you try to make repositories interchangeable to ease that pain, it then feels like you're *adding* friction up-front, just to prevent having to do more work later.



I feel like the happy ending here would simply be not to spend time switching to a RDBMS. Why not just use db40 in production? If someone wants a RDBMS in the picture, write ETL scripts to export to a warehouse :) Their dRS replication system seems designed for just that task.
Gravatar
robconery - Thursday, February 12, 2009 - You dump your reporting stuff into a SQL Db :)
Gravatar
Bruno - Thursday, February 12, 2009 - My concern has always been Reporting. How do you run reports from a OODB? Its not like you can connect Crystal Reports or Reporting Services to it... right?
Gravatar
Fregas - Tuesday, February 03, 2009 - The problem I have with DB40 is their licensing model. Its complicated and expensive for a lone developer. But until we lone developers can use it for our side projects and stuff, we won't be able to convince big companies to ditch the RDBMS for everything and use this crazy object stuff.
Gravatar
Nick - Wednesday, November 12, 2008 - Some news about that? Thanks.
Gravatar
justrudd - Friday, November 07, 2008 - I'm just being slow today, but I can't parse this comment. Are you saying write the RDBMS repos at the same time you are writing the OODBMS? Or after you've figured out the details of your model using OODBMD, then write the repo for RDBMS?
Gravatar
DougWilson - Tuesday, November 04, 2008 - I think you're onto something with this. I've been a "database first" guy since ASP classic in the 90s and I've always felt like I was "doing it wrong".



I recently began a project at work and am doing my best to stick to TDD as inspired by your MVC Storefront stuff. However, I keep finding myself getting pulled back to the database. At the very least what you're proposing here would remove the temptation of the database, at least initially, and allow me to focus more strictly on tests, implementing the model and the UI until I have something that is ready.
Gravatar
Jon Galloway - Tuesday, November 04, 2008 - Looks like db4o doesn't work in Silverlight quite yet since Silverlight doesn't support SerializableAttribute. Drat.
Gravatar
Nick - Wednesday, November 05, 2008 - Rob, could you elaborate on how, with a SqlRepository for instance, you can do the mapping between the types in your ORM and the types in your domain model. would need to be a type generated by L2S (or any other ORM) and at final you need to return let's say IQueryable (Product being defined in your Models folder). Moreover, I can't see how an SqlRepository could be directly injected by DI since it's generic.



Nore sure I'm very clear (english is not my first language). In other terms I would like to write a ProductRepository that takes an injected SqlRepository, but genericity prevents me to do it. In that case, I don't see what this new method changes compared to what you do in the Storefront where you also have nice interfaces (like ICatalogRepository) that already decouples your code from the persistence layer.



Thanks

Gravatar
browniepoints - Monday, November 03, 2008 - I followed along fully and nodded my head with you along the way. It's bold, and people WILL say you're crazy. But then again, what's so crazy about working efficiently.



What are your thoughts on using a double mapping model...where you make simple mappings from your database to your O/RM and then map from that to your Domain Model? In this case your Repository could be over the entity model and it return Domain Model objects.
Gravatar
Ayende Rahien - Monday, November 03, 2008 - Rob,

One big problem is that for most applications, trying to change OODB to RDBMS would not work without a LOT of work.
Gravatar
Ayende Rahien - Monday, November 03, 2008 - Oh, and your comments doesn't work in Safari
Gravatar
David Nelson - Monday, November 03, 2008 - My first question is, if you are still in the development and unit testing phase, why do you need a database at all? Just as a place to cache objects? Seems like there are easier ways.



Ayende, you make a nice broad negative statement there, without anything to back it up. Why does moving from OODB to RDBMS necessarily involve any more work than designing the schema and mapping it to your object model, which is the exact same work you would do if you started with the RDBMS?
Gravatar
robconery - Monday, November 03, 2008 - No thoughts on that - I've never used :(
Gravatar
robconery - Monday, November 03, 2008 - Hi Oren - if you implemented IRepository as I've done here, how would this not work? Can you be specific in terms of "a lot of work" and what that means?
Gravatar
robconery - Monday, November 03, 2008 - Weird cause they work on Chrome - which uses Webkit
Gravatar
robconery - Monday, November 03, 2008 - Hi David - true enough but at some point you have to have a demo app up and also Alpha/Beta rounds where you'll need to persist some data. It's my feeling that you can probably use an OODB for this.
Gravatar
Liam McLennan - Monday, November 03, 2008 - I think Ayende's point is that you would have to re-implement IRepository, which is a considerable task.
Gravatar
robconery - Monday, November 03, 2008 - Five methods? Really? How is that considerable?
Gravatar
BlackMael - Monday, November 03, 2008 - This was a great and thought provoking read. Thank you Rob. I am yet another step closer to finally stepping up onto the TDD wagon with this and the MVC Storefront series.

Gravatar
Damien Guard - Monday, November 03, 2008 - Love this idea even more than I did this afternoon when you explained it to me :)



[)amien
Gravatar
Amos Olson - Wednesday, November 05, 2008 - I have the exact same question (I think).



Rob, can you demonstrate how to implement IRepository with the double-mapping pattern? For example, could you demonstrate how to implement IRepository with the data access approach in the MVC storefront? It seems like T comes from the Model and the Repository only understands the ORM generated "version" of the class... is there some magic mapping that can happen between the types?
Gravatar
justrudd - Monday, November 03, 2008 - Depending on what you SQL looks like, it is actually quite easy. But if you go immediately to T-SQL hints (UPDLOCK, TABLELOCK, etc.), it will be very painful. I ported a SQL 2000 backed project to Oracle 8i in about a week because I wrote all my SQL in ANSI SQL. The only thing I had to rewrite were inner joins (dunno why Oracle was so late to the game to support the INNER JOIN syntax).
Gravatar
justrudd - Monday, November 03, 2008 - yes and no. I built out a prototype in 2001 using POET. I was able to port over the OLTP portions in a few days because of good abstractions (and this was with C++). Now the OLAP stuff took weeks. Primarily because all the OLAP tools we built around POET had to be thrown away and rewritten from scratch. If I had to do it over again, I'd have a secondary system that would go through the POET DB every once and awhile and convert directly to a RDBMS data warehouse. But I was young and knew no better.
Gravatar
justrudd - Monday, November 03, 2008 - +1 for real data. It is much simpler to create some good data in a single file and move it around. You can do this with SQL Compact Edition (which is what I used), but I think db4o would be better because you aren't wasting a lot of time writing the ORM or the mapping that your ORM needs. If you were truly adventurous, you could probably build a simple tool that took the db4o data and objects and creates your RDBMS tables for you. At least the first cut of them.
Gravatar
JontyMC - Monday, November 03, 2008 - Would this code imply that you need to change your entire object model to LINQ to SQL entities?
Gravatar
robconery - Monday, November 03, 2008 - Not necessarily - you could implement your own mapping bits like I did with the MVC Store.
Gravatar
Steven Sanderson - Monday, November 03, 2008 - Totally agree. I can't imagine that we'll still be using RDBMS for line-of-business apps in 20 years - it's such a painful 1980s technology, not really suitable for the way we build software today. I get the feeling that OODBMS is still too nascent for the mainstream, but something like it is inevitable. Perhaps massively scalable document-oriented databases (e.g., CouchDB) are going to find a niche along the road to full object storage.



Still, most businesses I work with are fanatically committed to relational databases. Looks like they'll still be the default in 10 years. There's something about databases that inspires people to be conservative and to accept change only on a geological timescale.
Gravatar
Liam McLennan - Tuesday, November 04, 2008 - A week I would call considerable.
Gravatar
Keith Stenson - Tuesday, November 04, 2008 - Rob, Just wanted to say that this was a exellent post. I have to admit I haven't given OODB's much thought but you're post and codes sample were great and gave me a lot to chew on. Looking forward to playing around with some of this when I get home tonight.





Gravatar
robconery - Wednesday, November 05, 2008 - Good questions :). You might have to roll a repository old-school; I was thinking you could just use a map here but as I'm trying to work the code out, it doesn't want to cooperate :).



Lemme work on this a minute - I have some ideas...
Gravatar
rgoytacaz - Tuesday, November 04, 2008 - Brilliant. Great post Rob.

What's the level of maturity of such project?

Why did you keep mentioning to not think about performance/scalling? What's the issues with perf/scalling using OODB's?

Could this be used in a RL scenario? For example StoreFront, would it be up to the task?
Gravatar
Mitch - Tuesday, November 04, 2008 - Interesting idea. It also provides a low usage version of your app without the need for an rdbms.



the only thing is that you don't get the benefit of testing the rdbms as you develop.



Thanks for sharing your thoughts.



Gravatar
brad dunbar - Tuesday, November 04, 2008 - I LOVE this idea. I think this is the very reason that its ridiculously cool to have the same interface for LINQ-to-objects and LINQ-to-SQL. Write code now, worry about storage when you need storage. In fact, don't worry about storage at all, let the storage guys worry about that and just plug it in.



Like you said, it sounds crazy but some of us just plain don't care about storage whatsoever. Users don't care about storage and as a result neither do I.



By the way, this is a particularly great point of view from someone who knows so much about ORM. Keep pushing this.
Gravatar
Jarrett - Wednesday, November 05, 2008 - Also, from what I've read the IQueryable "magic bullet" doesn't work with all providers. For example, the filter strategy found in storefront doesn't work with the entity framework.
Gravatar
Karthik Hariharan - Tuesday, November 04, 2008 - I've had this thought for a while, and your approach certainly makes sense. But I have to agree with Oren here. Right now it seems trivial to switch from OODB to RDBMS with just one interface to re-implement. But I could see this switching being very difficult with complex data models and relational data. Performance is also something that can be very punishing to those who don't think about it till the very end.



Also this brings up another point...what if you have to support an existing DB schema? If you've been working "un-teathered" with your own data model that makes sense just within your app's domain, you may have a LOT of work ahead of you to re-work your data model to perform well with your legacy database.

Gravatar
Ryan Roberts - Wednesday, November 05, 2008 - Great stuff Rob. You can drive your tests even faster if you use Io(new MemoryIoAdapter()) on your db4o configuration to make it use a ram disk. It makes it perfectly reasonable to set up and tear down the whole object container before each test. It's great being able to do lightning fast simple state based testing without having to mock out the repository.



Version one of the system I am working on will be taking db4o into production, assuming it performance tests well. We are keeping the repository implementation even simpler than yours (only fetching by aggregates by identifier) and using lucene for complicated queries.

Gravatar
skain - Wednesday, November 05, 2008 - What about the Linq To Sql DataLoadOptions stuff? Specifically I'm thinking of LoadWith and AssociateWith. These are essential in a multi-tiered application but your solution doesn't appear to address these needs at all. But db4o is interesting nonetheless. I just think you're underestimating the real difficulties that you'll encounter when you go to convert your db4o app over to Linq To Sql.
Gravatar
Ryan Roberts - Wednesday, November 05, 2008 - There's little or no need to consider lazy / eager loading issues with db4o, transparent activation is fast and simple: http://developer.db4o.com/blogs/product_news/archive/2008/01/10/from-transparent-activation-to-transparent-persistence.aspx. You can also manually control activation depth (eager loading) by configuring the container if you need to.



It's also a detail of the repository implementation, so it definitely doesn't need to be addressed if Rob is using db4o as a prototyping tool.
Gravatar
justrudd - Tuesday, November 04, 2008 - If time was the only cost, yes. But Oracle had cut us a deal on licensing and support costs. HP cut us a deal on hardware and support costs. A week of my time didn't equal the amount of money we were saving and the amount of scalability we gained by moving to a multi row version RDBMS. A week was well worth it to our project.
Gravatar
tomperren - Tuesday, November 04, 2008 - No 'Crazy Talk' here! All seems like a pragmatic approach to obliterating the most boring and wasteful part of software development these days... So: Who's going to write the auto-port from db4o objects to RDBMS tables ('first cut' - although, I'm sure with a few attributes added to the code, it could easily be the final cut - and simply a build step)... I see this as entirely possible, Rob - and in answer to your question: No you're not nuts ;) and, no, I cannot think of anything that stands between us and doing this.
Gravatar
skain - Wednesday, November 05, 2008 - Is he really talking about prototyping though? I get that it's not a problem for db4o. But it will be a problem when you go to port your app over to a relational server. And to me the point of this post was how easy it would be to use db4o early in the process and then switch over near the end. I just don't think the switch is going to be as easy as it's being made out to be.
Gravatar
robconery - Tuesday, November 04, 2008 - Hi Karthik - it is a very valid point that you would have some work to do - but a question I would ask you (and Oren) is wouldn't you need to do this anyway? And if you're doing it "along the way", as we do now, aren't you actually doing *more* than if you sat down and created your repo when you needed it?



I think a good example of this might be to see how hard it would be to apply IRepositorty to something like AdventureWorks. If you use SqlRepository and then inherit from it to something like SqlCatalogRepository, you should be able to map things quite easily. All in all, when I'm doing this with Linq To Sql and the Storefront, it doesn't take me too long (initially). What DOES take time is doing it over, and over, and over :).
Gravatar
robconery - Tuesday, November 04, 2008 - The reason I keep mentioning perf is the fact that I used a Singleton here, and that people like to focus on perf when talking about any data access. It's weird - "performance is a feature" only counts if you're not talking about data access :). It's usually the first thing out of someone's mouth whenever you discuss any ORM.



The fact of the matter is that OODBs actually hold the record for the largest, fastest database in history. They have very distinct advantages for simple data retrieval (no joins is one of em). There are issues - like any system. But mostly OODBs are more than up for the task.



In terms of maturity - DB4O is on version 7 and has been around 8 years. Other products (like Matisse and Versant) have been around longer and are big time industrial scale.



Matisse, for example, comes with a query manager that lets you write SQL :).
Gravatar
Charles Nurse - Wednesday, November 05, 2008 - I like this approach - been thinking along the same lines as you, although I was not aware of the Object Database you mentioned, which would make prototyping much easier.
Gravatar
Paul Houle - Tuesday, November 04, 2008 - Ugh. This is the worst case of "Visual Studio Syndrome" I've ever seen.



"Visual Studio Syndrome" is when people bang out a project quickly in visual studio, and then find it takes a few months to get the project working in a production environment. It's encouraged by a number of Microsoft tools that let you hit "F5" to run your project in a quirky development environment that has only a superficial resemblance to the environment that your product will run in.



Effective developers work in an environment that is as close to identical as the production environment as possible. It ought to be possible to build a copy of the development system for a new developer by following a checklist, and to build a new production system and move the data to it quickly. It ought to be possible to build a staging server that has a complete copy (or a big chunk) of the production system data so you can acceptance test changes before putting them into production.



You also need to consider the long-term evolution of the production system. There's going to be a day when you need to add a few new columns or a few tables to your database. It may be a few days, a few months or a few years after it goes into production, but it will happen if the system is successful. Your approach doesn't address the data migration issues, but sweeps them under the rug -- rather than solving the "two artifact" problem which is so deadly to ORM, it intensifies it.
Gravatar
robconery - Wednesday, November 05, 2008 - You know people keep saying this - I don't get it. Why in the world would it be harder to do in only once with a known set? If you roll up all the time you work on your ORM stuff anyway - how in the world would it be longer to do it at once?
Gravatar
robconery - Tuesday, November 04, 2008 - Hi Paul - thanks for the comment.



I honestly don't see what the tooling has to do with anything here. What I'm discussing is a development pattern that relates to architecture of the application. I'm trying to encourage TDD/DDD (as opposed to data-first, which is usually VS/RAD's focus).



>>Effective developers work in an environment that is as close to identical as the production environment as possible

Sure - I agree. Code has nothing to do with instrumentation does it?



>>>It ought to be possible to build a staging server that has a complete copy (or a big chunk) of the production system data so you can acceptance test changes before putting them into production

Agree. Are you assuming that you can't have test data in the OODB? If so, why not?



>>>There's going to be a day when you need to add a few new columns or a few tables to your database. It may be a few days, a few months or a few years after it goes into production, but it will happen if the system is successful. Your approach doesn't address the data migration issues, but sweeps them under the rug



How so? If you use an OODB to get yourself to Production, let's say, then you load your data into a relational structure in an RDBMS then won't you be at the exact same point? What's swept under the rug? You lost me here.









Gravatar
adweigert - Tuesday, November 04, 2008 - Yes you have, haven't you?! MVC StoreFront uses this model with DB -> LINQ to SQL -> OBJECT MODEL; or I could as well be crazy ...
Gravatar
rgoytacaz - Tuesday, November 04, 2008 - Some ppl can't think outside his box and tend to give vague arguments with beautiful words that in the ends means nothing. If you don't like it, in a discuss you should give a valid argument of why and how you solve THE problem in your world. Just like Rob did with his post.
Gravatar
Freek Felling - Tuesday, November 04, 2008 - Great post. I think this is great way for doing TDD.

As a developer I don't care about storage, why should I, it should just work. How hard can it be?



I would really like to see more discussion about this, keep up the good work.

Gravatar
Shane Courtrille - Wednesday, November 05, 2008 - One huge place that a certain ORM has is with single direction relationships. Say I have...



Player Table

PlayerId non-null

Name non-null

CurrentGameId non-null



Game

GameId non-null



If I deleted a Game it would blow up deleting the player because the only way it seems to work is by setting CurrentGameId = null first and then doing a delete. This of course blows up.
Gravatar
Chance - Wednesday, November 05, 2008 - Rob - this is absolutely brilliant. I've always wanted to try out an ORM but have heard horror stories so strayed away, so its really nice to know that theres one out there that works so effortlessly!



When I first started to deal with Domain Model, I always wanted to jump right in and crank out the schema so that I could have a solid 'foundation' to work off of. After a few projects, it became rather apparent that if you build out a schema first - you are tying your hands and really limiting your flexibility. You become bound to a schema that you designed before you even started coding!



On the other hand, if you ditch the schema until the very end, you are free to run rampant breaking and changing stuff as you please, which is more of my style anyways. The only downside I've encountered with postponing the schema until the end was having persistent data, and you just cured that, THANKS!





Quick side question: what blogging platform is this running on (assuming that its not custom built)?

Thanks,

Chance
Gravatar
Kevin Dente - Tuesday, November 04, 2008 - I couldn't agree more about the friction caused by database stuff. This is an interesting option. I wonder if you could make it even simpler for a lot of cases - just serialize arrays of objects to disk. Maybe I'll play around with that a bit.
Gravatar
Jeremiah Peschka - Wednesday, November 05, 2008 - At first, I thought you were nuts and were arguing for the view of the database as a Hash Attribute Value table. Then I kept a level head and finished reading your article and you've got some very valid points. One of the greatest frustrations during development is having to change the database and migrate columns from one table to another or to re-engineer a complex relationship between tables during heavy development.



During development, managing these things is painful, especially if each developer is developing against a local copy of the database. You can even end up with two incompatible change scripts to the database.



My only concern with the approach that you outlined is that it doesn't immediately allow for including business rules in the database which is (according to some) where these types of things belong (for a variety of reasons that go beyond this comment).



On the whole, very sane and thought out argument. Even as a database developer I found myself agreeing with you by the end. It's important to have a stable product before attempting to create a well thought out and properly optimized database.
Gravatar
Daniel - Tuesday, November 04, 2008 - DB4O is something I keep looking at every year and putting back down for one reason or another. Last year, it was that this cool LINQ-to-SQL thing was going to solve all my ORM issues. =) Nowadays I'm interested in these event-driven guys that say to avoid RDBMSes altogether and just stuff everything in distributed cache. But, spoiled by LINQ-to-SQL, I'm waiting for LINQ-to-Velocity =) I'll definitely be taking a look again though.



In the meantime, I'm still having trouble getting my head out of the RDBMS. Complex joins always tend to break down my model. For example - say given the above you want to get the Suppliers who have products that were reviewed by a given email:



IRepository rep = ...;

var suppliers = rep.Find((s) => ???);





Gravatar
Chance - Wednesday, November 05, 2008 - Woops - fingers didn't cooperate on that acronym, meant to say OODBM.. not ORM

reply
Gravatar
Dave Neeley - Friday, November 07, 2008 - The Gallio/mbUnit project has included a db40 implemenation in their 3.0.4 release. Their tentatively calling in Gallio.Ambience. You can read about it here: http://blog.bits-in-motion.com/2008/10/announcing-gallio-and-mbunit-v304.html
Gravatar
robconery - Wednesday, November 05, 2008 - This is Graffiti :)
Gravatar
robconery - Tuesday, November 04, 2008 - Oh - LOL yah :). Oops didn't know there was a name for it :). I like it! I'm using it and it works nicely - to me it's more flexible than XML mapping as it let's you use code :).
Gravatar
robconery - Tuesday, November 04, 2008 - You can query pretty deeply, but yes, you'd need to be careful about how deep you go as it can (with Linq To Sql especially) cause instantiation of every "touched object'.



But that doesn't matter during development :).
Gravatar
Johan Bergens - Tuesday, November 04, 2008 - Great article!



I took a short look at oodbms about 10 years ago. At that time they weren't really mature enough for larger applications and "real world" use. Has anyone looked at others systems than db4o lately?
Gravatar
Christian Maslen - Tuesday, November 04, 2008 - Excellent post. I never thought about switching persistence technologies between development and other environments - only persistence editions (eg SQL Server Dev edition locally, Enterprise or Standard edition for test to prod). I think it has merit for small system development or perhaps an application developed from scratch where the decision to move to the DB even for development can be made later. However Oren and Liam make good points.



The IRepository interface can serve a large part of your persistence needs but as they say there will be many scenarios in a large system in terms of both domain complexity and volume of data. Two examples of queries that aren't a good fit for Linq or any other ORM are SQL that uses case expressions or nested table expressions or relational OLAP constructs, but such queries (at least in my experience) are a small part of the overall code base and can be isolated in your persistence interface. Perhaps the IVeryNastyQueriesRepository ;o) That way the databasephobics can ignore that part of the system or refer the problem to the UserInterfacephobics.



Ignoring the database implementation until the very end - again in a large system - is like ignoring any other key piece of integration your system may require. It poses a significant risk and as such should be addressed as early in the project as possible. There are a number of database tools out there that work well with the process of refactoring your application - http://www.liquibase.org/ is currently my favourite tool for this. Such a tool is easy enough to incorporate into your build process so that rather than following a set of instructions a developer can check out the code, run the build script and be up and running with everything they need including test data.



Gravatar
PK - Tuesday, November 04, 2008 - Great post Rob!



Being blond, I got lost in a few spots towards the end .. so i'll need to read it a dozen more times. That said, I really love the idea. Right now, i'm basing my current projects of your StoreFront code. So for me, i've got TestRepositories which have hard coded stuff. My latest project has various 'pages' and is all working funky-dory .. and i haven't even created (nor thought about) generating my SqlRepository just yet. That's the _very very last_ thing on my mind and scheduled in.



Having to NOT WORRY about a Sql repository is a blessing because we can worry about getting the application RIGHT. Persistence is a requirement, but it's so not a worry to initially implement, that it can be delt with at the end of the development cycle IMO. Getting the business logic right and communicating to the user (ie. UX) the requirements should be delt with first. That's what's initially important.



Would it a long shot to maybe ask the StoreFront to get an update .. to have DB40 magic? At least part of the project? *beg and grovel?*



-PK-
Gravatar
robconery - Tuesday, November 04, 2008 - Hi Christian - thank for the comment...



>>>Ignoring the database implementation until the very end - again in a large system - is like ignoring any other key piece of integration your system may require

It's a fair point - but honestly won't you need to address the very same issue anyway? If you have a complex data interaction - it will happen at some point even if you ignore it using OODB. Which would you prefer: to have spent a bunch of time developing an ORM only to find out you now have to change in mid-course because of this complexity, or to let the dev process roll out and know precisely how to work with the problem at hand?



I don't think focusing on good OO architecture will be bad for your DB. And if it is - which is more important? Ahhh the million dollar question :).





Gravatar
Christian Maslen - Tuesday, November 04, 2008 - >>>It's a fair point - but honestly won't you need to address the very same issue anyway?



Only if I've never used an ORM before on a large system - otherwise there'd be some serious prototyping to do, but I take your point. My take is I don't see this as an either or situation - more of a nice approach to consider and use for as long as it works. I think focusing on good OO architecture, good back end relational design and prudent risk management are all important. What is most important depends on the project requirements.
Gravatar
browniepoints - Thursday, November 06, 2008 - So I saw something that made me come back to this post...I think there's a lot of congruence between what you're discussing and what is being said in these two posts.

http://seesharper.wordpress.com/2008/11/05/the-great-data-context-in-the-cloud/

http://seesharper.wordpress.com/2008/11/06/the-great-data-context-in-the-cloud-part-ii-synchronisation/
Gravatar
Chance - Thursday, November 06, 2008 - Thanks.



Also, the MTG card brought back some memories!
Gravatar
William Gant - Thursday, November 06, 2008 - They don't work at my office, but they work at home. I believe it's the crummy web filtering software we have.
Gravatar
Kevin Babcock - Friday, November 14, 2008 - Best blog post I've read in a long time! I can't wait to try this out on a new (small) project I was hoping to knock out this weekend.
Gravatar
pmlarocque - Saturday, November 15, 2008 - 1- Biggest downside for me is that doing thing the agile way, I need working software at the end of every iteration. Yes you can argue that a system with a OODBs backend is working, but most customer wont like it.



2- And I am currently taking over a project done in this fashion, ( but in memory Reposity with fillers to make necessary data avail) and the Model for the most complex part is just made in a way that you cannot translate it to a RDMS mapping.



My first point is my biggest concern, the second is mainly due to inexperience developping with this approach, but it would be for sure a pitfall to watch.



Great post!

Gravatar
hodzanassredin - Sunday, November 16, 2008 - Hi. Thanks a lot for Storefront MVC and sory my english :-) I I'm trying to build some repository and filters for my application like your did it in Storefront. But i have big problem: _rep.GetCategories().WithId(someId) invokes select * from Categories in db and after that apllying filter but this is very bad for performance and i prefer to invoke select * from Categories where Id == someId. What do you think about it? As i understand select in GetCategories creates new objects to compare with filter but we need to inject filter into query.
Gravatar
robconery - Sunday, November 16, 2008 - The customer didn't like the OODB? That's agile? Hmmmm



Also I think it's weird that you can't map a model to an RDBMS.



Sent from my phone. Please excuse brief replies.
Gravatar
robconery - Sunday, November 16, 2008 - Please put these comments on the storefront posts. Linq doesn't do

select * so no worries there. Also delayed execution applied the

filter first, not after.
Gravatar
pmlarocque - Sunday, November 16, 2008 - Well from a development point of view using OODB and the approach you describe is indeed agile. But from a customer point of view, let say you sold with agile advantage like the possibility to stop a project at the end of any iteration and get working software up to the user stories you have done. Well if they expect a MS SQL backend, stop the project in a random iteration. Well they wont get the working software they expect. Ok I admit this case is more the exception than the rule.



For the Model I could not map to an RDBS, well persisting data to a OODB is so simple, they persisted a part that does not belong to persistence but should be computed on demand, or you end up with redundant data across your database. But now swicthing repository involve a lot of work being able to reconstuct complex object from other entities, this should have been spot sooner.
Gravatar
david_a_r_kemp - Monday, November 17, 2008 - I think this is a brilliant idea, but I'd refer you to Joel's Leaky Abstraction post (http://www.joelonsoftware.com/articles/LeakyAbstractions.html).



In my experience, there's always a bit of give-and-take between the DB design/ORM/Object model (we're not all Ayende - and can't all convince our customers that running off of open source trunk code is a good idea), and you're likely to encounter a case where something that you can do with objects can't be done with DB4O, or you can do it with DB4O, but not your ORM, etc...



Also, your object design might have to be changed to get the performance you need. I'd hope this is a really edge case, but I had a certain project where a Many-to-One relationship ended up being stored by passing XML to SQL, and then having a trigger on an XML column. Nasty, nasty stuff, but it was sooo much quicker than the ORM's implementation of storing a Many-to-one relationship.



There are always cases where how you do something is influenced by which tools you're using to do it (compare ASP.NET with ASP.NET MVC, and compare ASP.NET MVC with Monorail), and not using the target technologies to develop against means you're going to hit problems later.



And, yes, performance is a development concern. No matter what you think.
Gravatar
robconery - Monday, November 17, 2008 - Hi David - There is no perfect ORM - that's for sure. NHib is probably as close as we can get to a mature relational mapper but there are still problems. I do agree there. However people will still try to do it - try to make a model that is OO and map it to a relational DB. My thought here is why not start with a known set? Start with your model (instead of your DB) and then adjust your DB. App first is the idea here.



I've always been a perf nut, but only over the last 2.5 years have I been swayed to address perf when needed. And believe it or not - 9 times out of 10 it's not that big a deal. More often perceived slowdowns are from too much client code (js libraries, etc); almost never a client call.



Perf shouldn't really be handled until you need to (or until a test id's a problem). ADO is really fast these days - and attributing bottlenecks to DB calls is really something from 4-5 years ago.
Gravatar
david_a_r_kemp - Tuesday, November 18, 2008 - "attributing bottlenecks to DB calls is really something from 4-5 years ago."



Attributing performance to DB Queries is still a big issue, especially when you're dealing with millions of rows.
Gravatar
dorian_farrimond - Thursday, November 20, 2008 - I like this idea because it's a step in the right direction. Sure it's a bit of a pain to implement the RDBMS at the end but like you say at least the app is developed with a pure OO mindset and relational issues don't creep into the object model.



By doing this it should eventually drive the tools to catch up with development methods. We might get to object persistence nirvana a little bit sooner than we otherwise would.
Gravatar
Jason - Friday, November 28, 2008 - I tried experimenting with this idea, but am roadblocked with trying to use System.Transaction.TransactionScope... (Not all that familiar w/ the repository pattern yet, but was hoping to use db4o to start practicing some of these techniques)... Any thoughts on how to get TransactionScope to work seamless with DB4O?
Gravatar
robconery - Friday, November 28, 2008 - Wrap the call in using(System.Transaction)? Some details might help...
Gravatar
Jason - Friday, November 28, 2008 - I'm not sure what you mean by "using(System.Transaction)" since that doesn't seem to exist, and System.Transaction[s] is a namespace.



There are a couple links that look like feature requests on the Db4o site



http://tracker.db4o.com/browse/COR-1376

http://tracker.db4o.com/browse/COR-1143



related to TransactionScope.



Below is the unit test I would like to figure out how to make pass.



[Test]

public void CanRollbackSavedObjectInRepository()

{

var newObject = new TestObject()

{

SomeInteger = 1,

SomeString = "string here",

};



using (TransactionScope scope = new TransactionScope())

{

_repository.Save(newObject);



if (_repository.GetAll().Count() != 1)

Assert.Fail("Singe item was not saved to the object database. itemCount = " + _repository.GetAll().Count().ToString());



//scope.Complete();

}



if (_repository.GetAll().Count() > 0)

Assert.Fail("TransactionScope Failed");

}



Any ideas?



Gravatar
Jason - Friday, November 28, 2008 - I think I'm making some headway...



I found a project at http://code.google.com/p/uniframework/



In this project there's an implementation of IEnlistmentNotification called Db4oEnlist and in my repository I can do something like this...



public void Save(T item)

{

Db4oEnlist enlist = new Db4oEnlist(this.Container, item);

bool inTransaction = Enlist(enlist);

this.Container.Store(item);

if (!inTransaction)

this.Container.Commit();

}



the Enlist method looks like...



private bool Enlist(Db4oEnlist enlist)

{

System.Transactions.Transaction currentTx = System.Transactions.Transaction.Current;

if (currentTx != null)

{

currentTx.EnlistVolatile(enlist, EnlistmentOptions.None);

return true;

}

return false;

}



Unless there's a better way out there to do this using TransactionScope... I was able to get my unit test above to pass...
Gravatar
robconery - Friday, November 28, 2008 - using(System.Transaction) was shorthand - I'm going to guess you know what I

mean :). Are you saying that wrapping DB4O calls in a transaction scope

won't work?

That said, I know DB40 will support them as well.
Gravatar
Jason - Saturday, November 29, 2008 - Yes i'm saying wrapping DB4O calls in TransactionScope won't work... Unless you do the work I put above (basically implementing the goo by yourself)..



not sure what you mean by "I know DB40 will support them as well."
Gravatar
mike - Wednesday, December 03, 2008 - What's really cool is this:

from Product p in DB.Container



instead of this

from Product p in context.Products



So, the products are just in the container, no indirection needed.



I will test it out for sure. It might be a goo option for a single user application instead og SQLite+NHibernate, like you said, stop worrying about all that.
Gravatar
MortMan79 - Sunday, December 07, 2008 - To try out different ORM/data access approaches I decided to implement your IRepository for a small application. My idea was to implement Db4o then Linq2SQL then SubSonic 3 and see which I liked the best. So far I managed to refactor the existing data access to use IRepository. The existing data access uses Oracle, simple selects/SPs to retrieve and update data from two tables. So far I have implemented my Db4oRepository and I have encountered a fewproblems:



1. Db4o does not take kindly to multiple web sites sharing a database file. This is per design but is really a show stopper since my application consists of two distinct websites operating on the same data. Also Db4o seems to get confused saving/loading complex hierarchies of references. Loading a list (100 elements) that references a set of elements which each reference each other through a tree took 10 seconds to load.



2. It could be that I am too used to SQL, but all my objects have one or more IDs that are either their own ID or a reference. These IDs are normally assigned by the database, but Db4o does not assign IDs. So to use both SQL and Db4o I need to either make Db4o assign IDs like SQL and thus know about which field is an ID or I have to hide the IDs. Neither solution is attractive - making Db4o assign IDs seems the safest.



3. The next problem I encountered was that my Db4o repository only needs to work on one type while my Oracle repository needs to work on two types (remember I have 2 tables). This is because Db4o stores a whole object including references and references references. This is a cool feature, but SQL works differently and really provides a model that is very different.





My conclusion is that your idea is sound, especially for a quick spike which needs some persistence. Db4o might also be useful for the initial stages of a website, but once you decide to switch to SQL then you will need to make several changes and you can't go back. I would not suggest trying to run a SQLRepository side by side with a Db4oRepository. The models are too different and the result feels wrong.
Gravatar
Chris - Sunday, December 07, 2008 - love the article i've also implemented it in a sample app that i developed just for giggles... i just have a question... it does provide persistance, however in my BDD drive out session for the repository i actually found that i drove out a static list repository. obviously the static repository won't last as long as an oodb it still made for an easier way of driving and testing db persistance without having to worry about persistence.
Gravatar
J Wynia - Tuesday, June 09, 2009 - Just a quick note in a slightly different direction than most of the comments. The locking shouldn't be necessary if you get your db4o handle via the OpenServer method instead of just opening the file. _db = Db4oFactory.OpenServer(_dbPath, -1).OpenClient(); I haven't done much with it to be certain, but that method has been in 2 of the articles I've read so far for setting up more of a "server" approach and not have to deal with the locking in the Repository class directly. Given that lots of my prototypes are using AJAX to make asynchronous calls to get data, the file lock in the original version had me a little concerned.
Gravatar
browniepoints - Monday, November 03, 2008 - I followed along fully and nodded my head with you along the way. It's bold, and people WILL say you're crazy. But then again, what's so crazy about working efficiently.

What are your thoughts on using a double mapping model...where you make simple mappings from your database to your O/RM and then map from that to your Domain Model? In this case your Repository could be over the entity model and it return Domain Model objects.
Gravatar
robconery - Tuesday, November 04, 2008 - No thoughts on that - I've never used :(
Gravatar
Adam Weigert - Tuesday, November 04, 2008 - Yes you have, haven't you?! MVC StoreFront uses this model with DB -> LINQ to SQL -> OBJECT MODEL; or I could as well be crazy ...
Gravatar
robconery - Tuesday, November 04, 2008 - Oh - LOL yah :). Oops didn't know there was a name for it :). I like it! I'm using it and it works nicely - to me it's more flexible than XML mapping as it let's you use code :).
Gravatar
Ayende Rahien - Monday, November 03, 2008 - Rob,
One big problem is that for most applications, trying to change OODB to RDBMS would not work without a LOT of work.
Gravatar
robconery - Tuesday, November 04, 2008 - Hi Oren - if you implemented IRepository<T> as I've done here, how would this not work? Can you be specific in terms of "a lot of work" and what that means?
Gravatar
Justin Rudd - Tuesday, November 04, 2008 - yes and no. I built out a prototype in 2001 using POET. I was able to port over the OLTP portions in a few days because of good abstractions (and this was with C++). Now the OLAP stuff took weeks. Primarily because all the OLAP tools we built around POET had to be thrown away and rewritten from scratch. If I had to do it over again, I'd have a secondary system that would go through the POET DB every once and awhile and convert directly to a RDBMS data warehouse. But I was young and knew no better.
Gravatar
Ayende Rahien - Monday, November 03, 2008 - Oh, and your comments doesn't work in Safari
Gravatar
robconery - Tuesday, November 04, 2008 - Weird cause they work on Chrome - which uses Webkit
Gravatar
William Gant - Thursday, November 06, 2008 - They don't work at my office, but they work at home. I believe it's the crummy web filtering software we have.
Gravatar
David Nelson - Monday, November 03, 2008 - My first question is, if you are still in the development and unit testing phase, why do you need a database at all? Just as a place to cache objects? Seems like there are easier ways.

Ayende, you make a nice broad negative statement there, without anything to back it up. Why does moving from OODB to RDBMS necessarily involve any more work than designing the schema and mapping it to your object model, which is the exact same work you would do if you started with the RDBMS?
Gravatar
robconery - Tuesday, November 04, 2008 - Hi David - true enough but at some point you have to have a demo app up and also Alpha/Beta rounds where you'll need to persist some data. It's my feeling that you can probably use an OODB for this.
Gravatar
Justin Rudd - Tuesday, November 04, 2008 - +1 for real data. It is much simpler to create some good data in a single file and move it around. You can do this with SQL Compact Edition (which is what I used), but I think db4o would be better because you aren't wasting a lot of time writing the ORM or the mapping that your ORM needs. If you were truly adventurous, you could probably build a simple tool that took the db4o data and objects and creates your RDBMS tables for you. At least the first cut of them.
Gravatar
Liam McLennan - Tuesday, November 04, 2008 - I think Ayende's point is that you would have to re-implement IRepository<T>, which is a considerable task.
Gravatar
robconery - Tuesday, November 04, 2008 - Five methods? Really? How is that considerable?
Gravatar
Liam McLennan - Tuesday, November 04, 2008 - In my applications I find that I need to extend IRepository to more specialised repositories, like IProductRepository : IRepository<Product>. In IProductRepository I have a heap of data access methods that are specific to products. It is re-implementing those methods that would take the effort. If both persistence technologies implement Linq in exactly the same way then it might be trivial, but that seems unlikely. It's like the classic migration between Sql Server and Oracle. They're both sql databases so it should be easy, right?
Gravatar
Justin Rudd - Tuesday, November 04, 2008 - Depending on what you SQL looks like, it is actually quite easy. But if you go immediately to T-SQL hints (UPDLOCK, TABLELOCK, etc.), it will be very painful. I ported a SQL 2000 backed project to Oracle 8i in about a week because I wrote all my SQL in ANSI SQL. The only thing I had to rewrite were inner joins (dunno why Oracle was so late to the game to support the INNER JOIN syntax).
Gravatar
Liam McLennan - Tuesday, November 04, 2008 - A week I would call considerable.
Gravatar
Justin Rudd - Tuesday, November 04, 2008 - If time was the only cost, yes. But Oracle had cut us a deal on licensing and support costs. HP cut us a deal on hardware and support costs. A week of my time didn't equal the amount of money we were saving and the amount of scalability we gained by moving to a multi row version RDBMS. A week was well worth it to our project.
Gravatar
BlackMael - Tuesday, November 04, 2008 - This was a great and thought provoking read. Thank you Rob. I am yet another step closer to finally stepping up onto the TDD wagon with this and the MVC Storefront series.
Gravatar
Damien Guard - Tuesday, November 04, 2008 - Love this idea even more than I did this afternoon when you explained it to me :)

[)amien
Gravatar
JontyMC - Tuesday, November 04, 2008 - Would this code imply that you need to change your entire object model to LINQ to SQL entities?
Gravatar
robconery - Tuesday, November 04, 2008 - Not necessarily - you could implement your own mapping bits like I did with the MVC Store.
Gravatar
Steven Sanderson - Tuesday, November 04, 2008 - Totally agree. I can't imagine that we'll still be using RDBMS for line-of-business apps in 20 years - it's such a painful 1980s technology, not really suitable for the way we build software today. I get the feeling that OODBMS is still too nascent for the mainstream, but something like it is inevitable. Perhaps massively scalable document-oriented databases (e.g., CouchDB) are going to find a niche along the road to full object storage.

Still, most businesses I work with are fanatically committed to relational databases. Looks like they'll still be the default in 10 years. There's something about databases that inspires people to be conservative and to accept change only on a geological timescale.
Gravatar
Keith Stenson - Tuesday, November 04, 2008 - Rob, Just wanted to say that this was a exellent post. I have to admit I haven't given OODB's much thought but you're post and codes sample were great and gave me a lot to chew on. Looking forward to playing around with some of this when I get home tonight.
Gravatar
rgoytacaz - Tuesday, November 04, 2008 - Brilliant. Great post Rob.
What's the level of maturity of such project?
Why did you keep mentioning to not think about performance/scalling? What's the issues with perf/scalling using OODB's?
Could this be used in a RL scenario? For example StoreFront, would it be up to the task?
Gravatar
robconery - Tuesday, November 04, 2008 - The reason I keep mentioning perf is the fact that I used a Singleton here, and that people like to focus on perf when talking about any data access. It's weird - "performance is a feature" only counts if you're not talking about data access :). It's usually the first thing out of someone's mouth whenever you discuss any ORM.

The fact of the matter is that OODBs actually hold the record for the largest, fastest database in history. They have very distinct advantages for simple data retrieval (no joins is one of em). There are issues - like any system. But mostly OODBs are more than up for the task.

In terms of maturity - DB4O is on version 7 and has been around 8 years. Other products (like Matisse and Versant) have been around longer and are big time industrial scale.

Matisse, for example, comes with a query manager that lets you write SQL :).
Gravatar
Mitch - Tuesday, November 04, 2008 - Interesting idea. It also provides a low usage version of your app without the need for an rdbms.

the only thing is that you don't get the benefit of testing the rdbms as you develop.

Thanks for sharing your thoughts.
Gravatar
brad dunbar - Tuesday, November 04, 2008 - I LOVE this idea. I think this is the very reason that its ridiculously cool to have the same interface for LINQ-to-objects and LINQ-to-SQL. Write code now, worry about storage when you need storage. In fact, don't worry about storage at all, let the storage guys worry about that and just plug it in.

Like you said, it sounds crazy but some of us just plain don't care about storage whatsoever. Users don't care about storage and as a result neither do I.

By the way, this is a particularly great point of view from someone who knows so much about ORM. Keep pushing this.
Gravatar
Karthik Hariharan - Tuesday, November 04, 2008 - I've had this thought for a while, and your approach certainly makes sense. But I have to agree with Oren here. Right now it seems trivial to switch from OODB to RDBMS with just one interface to re-implement. But I could see this switching being very difficult with complex data models and relational data. Performance is also something that can be very punishing to those who don't think about it till the very end.

Also this brings up another point...what if you have to support an existing DB schema? If you've been working "un-teathered" with your own data model that makes sense just within your app's domain, you may have a LOT of work ahead of you to re-work your data model to perform well with your legacy database.
Gravatar
robconery - Tuesday, November 04, 2008 - Hi Karthik - it is a very valid point that you would have some work to do - but a question I would ask you (and Oren) is wouldn't you need to do this anyway? And if you're doing it "along the way", as we do now, aren't you actually doing *more* than if you sat down and created your repo when you needed it?

I think a good example of this might be to see how hard it would be to apply IRepositorty<T> to something like AdventureWorks. If you use SqlRepository<T> and then inherit from it to something like SqlCatalogRepository, you should be able to map things quite easily. All in all, when I'm doing this with Linq To Sql and the Storefront, it doesn't take me too long (initially). What DOES take time is doing it over, and over, and over :).
Gravatar
tomperren - Tuesday, November 04, 2008 - No 'Crazy Talk' here! All seems like a pragmatic approach to obliterating the most boring and wasteful part of software development these days... So: Who's going to write the auto-port from db4o objects to RDBMS tables ('first cut' - although, I'm sure with a few attributes added to the code, it could easily be the final cut - and simply a build step)... I see this as entirely possible, Rob - and in answer to your question: No you're not nuts ;) and, no, I cannot think of anything that stands between us and doing this.
Gravatar
Paul Houle - Tuesday, November 04, 2008 - Ugh. This is the worst case of "Visual Studio Syndrome" I've ever seen.

"Visual Studio Syndrome" is when people bang out a project quickly in visual studio, and then find it takes a few months to get the project working in a production environment. It's encouraged by a number of Microsoft tools that let you hit "F5" to run your project in a quirky development environment that has only a superficial resemblance to the environment that your product will run in.

Effective developers work in an environment that is as close to identical as the production environment as possible. It ought to be possible to build a copy of the development system for a new developer by following a checklist, and to build a new production system and move the data to it quickly. It ought to be possible to build a staging server that has a complete copy (or a big chunk) of the production system data so you can acceptance test changes before putting them into production.

You also need to consider the long-term evolution of the production system. There's going to be a day when you need to add a few new columns or a few tables to your database. It may be a few days, a few months or a few years after it goes into production, but it will happen if the system is successful. Your approach doesn't address the data migration issues, but sweeps them under the rug -- rather than solving the "two artifact" problem which is so deadly to ORM, it intensifies it.
Gravatar
robconery - Tuesday, November 04, 2008 - Hi Paul - thanks for the comment.

I honestly don't see what the tooling has to do with anything here. What I'm discussing is a development pattern that relates to architecture of the application. I'm trying to encourage TDD/DDD (as opposed to data-first, which is usually VS/RAD's focus).

>>Effective developers work in an environment that is as close to identical as the production environment as possible
Sure - I agree. Code has nothing to do with instrumentation does it?

>>>It ought to be possible to build a staging server that has a complete copy (or a big chunk) of the production system data so you can acceptance test changes before putting them into production
Agree. Are you assuming that you can't have test data in the OODB? If so, why not?

>>>There's going to be a day when you need to add a few new columns or a few tables to your database. It may be a few days, a few months or a few years after it goes into production, but it will happen if the system is successful. Your approach doesn't address the data migration issues, but sweeps them under the rug

How so? If you use an OODB to get yourself to Production, let's say, then you load your data into a relational structure in an RDBMS then won't you be at the exact same point? What's swept under the rug? You lost me here.
Gravatar
rgoytacaz - Tuesday, November 04, 2008 - Some ppl can't think outside his box and tend to give vague arguments with beautiful words that in the ends means nothing. If you don't like it, in a discuss you should give a valid argument of why and how you solve THE problem in your world. Just like Rob did with his post.
Gravatar
Freek Felling - Tuesday, November 04, 2008 - Great post. I think this is great way for doing TDD.
As a developer I don't care about storage, why should I, it should just work. How hard can it be?

I would really like to see more discussion about this, keep up the good work.
Gravatar
Kevin Dente - Tuesday, November 04, 2008 - I couldn't agree more about the friction caused by database stuff. This is an interesting option. I wonder if you could make it even simpler for a lot of cases - just serialize arrays of objects to disk. Maybe I'll play around with that a bit.
Gravatar
Daniel - Tuesday, November 04, 2008 - DB4O is something I keep looking at every year and putting back down for one reason or another. Last year, it was that this cool LINQ-to-SQL thing was going to solve all my ORM issues. =) Nowadays I'm interested in these event-driven guys that say to avoid RDBMSes altogether and just stuff everything in distributed cache. But, spoiled by LINQ-to-SQL, I'm waiting for LINQ-to-Velocity =) I'll definitely be taking a look again though.

In the meantime, I'm still having trouble getting my head out of the RDBMS. Complex joins always tend to break down my model. For example - say given the above you want to get the Suppliers who have products that were reviewed by a given email:

IRepository<Supplier> rep = ...;
var suppliers = rep.Find((s) => ???);
Gravatar
robconery - Tuesday, November 04, 2008 - You can query pretty deeply, but yes, you'd need to be careful about how deep you go as it can (with Linq To Sql especially) cause instantiation of every "touched object'.

But that doesn't matter during development :).
Gravatar
Johan Bergens - Tuesday, November 04, 2008 - Great article!

I took a short look at oodbms about 10 years ago. At that time they weren't really mature enough for larger applications and "real world" use. Has anyone looked at others systems than db4o lately?
Gravatar
Christian Maslen - Tuesday, November 04, 2008 - Excellent post. I never thought about switching persistence technologies between development and other environments - only persistence editions (eg SQL Server Dev edition locally, Enterprise or Standard edition for test to prod). I think it has merit for small system development or perhaps an application developed from scratch where the decision to move to the DB even for development can be made later. However Oren and Liam make good points.

The IRepository interface can serve a large part of your persistence needs but as they say there will be many scenarios in a large system in terms of both domain complexity and volume of data. Two examples of queries that aren't a good fit for Linq or any other ORM are SQL that uses case expressions or nested table expressions or relational OLAP constructs, but such queries (at least in my experience) are a small part of the overall code base and can be isolated in your persistence interface. Perhaps the IVeryNastyQueriesRepository ;o) That way the databasephobics can ignore that part of the system or refer the problem to the UserInterfacephobics.

Ignoring the database implementation until the very end - again in a large system - is like ignoring any other key piece of integration your system may require. It poses a significant risk and as such should be addressed as early in the project as possible. There are a number of database tools out there that work well with the process of refactoring your application - http://www.liquibase.org/ is currently my favourite tool for this. Such a tool is easy enough to incorporate into your build process so that rather than following a set of instructions a developer can check out the code, run the build script and be up and running with everything they need including test data.
Gravatar
robconery - Tuesday, November 04, 2008 - Hi Christian - thank for the comment...

>>>Ignoring the database implementation until the very end - again in a large system - is like ignoring any other key piece of integration your system may require
It's a fair point - but honestly won't you need to address the very same issue anyway? If you have a complex data interaction - it will happen at some point even if you ignore it using OODB. Which would you prefer: to have spent a bunch of time developing an ORM only to find out you now have to change in mid-course because of this complexity, or to let the dev process roll out and know precisely how to work with the problem at hand?

I don't think focusing on good OO architecture will be bad for your DB. And if it is - which is more important? Ahhh the million dollar question :).
Gravatar
Christian Maslen - Tuesday, November 04, 2008 - >>>It's a fair point - but honestly won't you need to address the very same issue anyway?

Only if I've never used an ORM before on a large system - otherwise there'd be some serious prototyping to do, but I take your point. My take is I don't see this as an either or situation - more of a nice approach to consider and use for as long as it works. I think focusing on good OO architecture, good back end relational design and prudent risk management are all important. What is most important depends on the project requirements.
Gravatar
PK - Tuesday, November 04, 2008 - Great post Rob!

Being blond, I got lost in a few spots towards the end .. so i'll need to read it a dozen more times. That said, I really love the idea. Right now, i'm basing my current projects of your StoreFront code. So for me, i've got TestRepositories which have hard coded stuff. My latest project has various 'pages' and is all working funky-dory .. and i haven't even created (nor thought about) generating my SqlRepository just yet. That's the _very very last_ thing on my mind and scheduled in.

Having to NOT WORRY about a Sql repository is a blessing because we can worry about getting the application RIGHT. Persistence is a requirement, but it's so not a worry to initially implement, that it can be delt with at the end of the development cycle IMO. Getting the business logic right and communicating to the user (ie. UX) the requirements should be delt with first. That's what's initially important.

Would it a long shot to maybe ask the StoreFront to get an update .. to have DB40 magic? At least part of the project? *beg and grovel?*

-PK-
Gravatar
Doug Wilson - Wednesday, November 05, 2008 - I think you're onto something with this. I've been a "database first" guy since ASP classic in the 90s and I've always felt like I was "doing it wrong".

I recently began a project at work and am doing my best to stick to TDD as inspired by your MVC Storefront stuff. However, I keep finding myself getting pulled back to the database. At the very least what you're proposing here would remove the temptation of the database, at least initially, and allow me to focus more strictly on tests, implementing the model and the UI until I have something that is ready.
Gravatar
Jon Galloway - Wednesday, November 05, 2008 - Looks like db4o doesn't work in Silverlight quite yet since Silverlight doesn't support SerializableAttribute. Drat.
Gravatar
Nick - Wednesday, November 05, 2008 - Rob, could you elaborate on how, with a SqlRepository for instance, you can do the mapping between the types in your ORM and the types in your domain model. <T> would need to be a type generated by L2S (or any other ORM) and at final you need to return let's say IQueryable<Product> (Product being defined in your Models folder). Moreover, I can't see how an SqlRepository<T> could be directly injected by DI since it's generic.

Nore sure I'm very clear (english is not my first language). In other terms I would like to write a ProductRepository that takes an injected SqlRepository, but genericity prevents me to do it. In that case, I don't see what this new method changes compared to what you do in the Storefront where you also have nice interfaces (like ICatalogRepository) that already decouples your code from the persistence layer.

Thanks
Gravatar
Amos Olson - Wednesday, November 05, 2008 - I have the exact same question (I think).

Rob, can you demonstrate how to implement IRepository<T> with the double-mapping pattern? For example, could you demonstrate how to implement IRepository<T> with the data access approach in the MVC storefront? It seems like T comes from the Model and the Repository only understands the ORM generated "version" of the class... is there some magic mapping that can happen between the types?
Gravatar
robconery - Wednesday, November 05, 2008 - Good questions :). You might have to roll a repository old-school; I was thinking you could just use a map here but as I'm trying to work the code out, it doesn't want to cooperate :).

Lemme work on this a minute - I have some ideas...
Gravatar
Nick - Wednesday, November 12, 2008 - Some news about that? Thanks.
Gravatar
Jarrett - Wednesday, November 05, 2008 - Also, from what I've read the IQueryable "magic bullet" doesn't work with all providers. For example, the filter strategy found in storefront doesn't work with the entity framework.
Gravatar
Ryan Roberts - Wednesday, November 05, 2008 - Great stuff Rob. You can drive your tests even faster if you use Io(new MemoryIoAdapter()) on your db4o configuration to make it use a ram disk. It makes it perfectly reasonable to set up and tear down the whole object container before each test. It's great being able to do lightning fast simple state based testing without having to mock out the repository.

Version one of the system I am working on will be taking db4o into production, assuming it performance tests well. We are keeping the repository implementation even simpler than yours (only fetching by aggregates by identifier) and using lucene for complicated queries.
Gravatar
skain - Wednesday, November 05, 2008 - What about the Linq To Sql DataLoadOptions stuff? Specifically I'm thinking of LoadWith<T> and AssociateWith<T>. These are essential in a multi-tiered application but your solution doesn't appear to address these needs at all. But db4o is interesting nonetheless. I just think you're underestimating the real difficulties that you'll encounter when you go to convert your db4o app over to Linq To Sql.
Gravatar
Ryan Roberts - Wednesday, November 05, 2008 - There's little or no need to consider lazy / eager loading issues with db4o, transparent activation is fast and simple: http://developer.db4o.com/blogs/product_news/ar.... You can also manually control activation depth (eager loading) by configuring the container if you need to.

It's also a detail of the repository implementation, so it definitely doesn't need to be addressed if Rob is using db4o as a prototyping tool.
Gravatar
skain - Wednesday, November 05, 2008 - Is he really talking about prototyping though? I get that it's not a problem for db4o. But it will be a problem when you go to port your app over to a relational server. And to me the point of this post was how easy it would be to use db4o early in the process and then switch over near the end. I just don't think the switch is going to be as easy as it's being made out to be.
Gravatar
robconery - Wednesday, November 05, 2008 - You know people keep saying this - I don't get it. Why in the world would it be harder to do in only once with a known set? If you roll up all the time you work on your ORM stuff anyway - how in the world would it be longer to do it at once?
Gravatar
Justin Rudd - Friday, November 07, 2008 - I'm just being slow today, but I can't parse this comment. Are you saying write the RDBMS repos at the same time you are writing the OODBMS? Or after you've figured out the details of your model using OODBMD, then write the repo for RDBMS?
Gravatar
Charles Nurse - Wednesday, November 05, 2008 - I like this approach - been thinking along the same lines as you, although I was not aware of the Object Database you mentioned, which would make prototyping much easier.
Gravatar
Shane Courtrille - Wednesday, November 05, 2008 - One huge place that a certain ORM has is with single direction relationships. Say I have...

Player Table
PlayerId non-null
Name non-null
CurrentGameId non-null

Game
GameId non-null

If I deleted a Game it would blow up deleting the player because the only way it seems to work is by setting CurrentGameId = null first and then doing a delete. This of course blows up.
Gravatar
Chance - Wednesday, November 05, 2008 - Rob - this is absolutely brilliant. I've always wanted to try out an ORM but have heard horror stories so strayed away, so its really nice to know that theres one out there that works so effortlessly!

When I first started to deal with Domain Model, I always wanted to jump right in and crank out the schema so that I could have a solid 'foundation' to work off of. After a few projects, it became rather apparent that if you build out a schema first - you are tying your hands and really limiting your flexibility. You become bound to a schema that you designed before you even started coding!

On the other hand, if you ditch the schema until the very end, you are free to run rampant breaking and changing stuff as you please, which is more of my style anyways. The only downside I've encountered with postponing the schema until the end was having persistent data, and you just cured that, THANKS!


Quick side question: what blogging platform is this running on (assuming that its not custom built)?
Thanks,
Chance
Gravatar
Chance - Wednesday, November 05, 2008 - Woops - fingers didn't cooperate on that acronym, meant to say OODBM.. not ORM
reply
Gravatar
robconery - Wednesday, November 05, 2008 - This is Graffiti :)
Gravatar
Chance - Thursday, November 06, 2008 - Thanks.

Also, the MTG card brought back some memories!
Gravatar
Jeremiah Peschka - Wednesday, November 05, 2008 - At first, I thought you were nuts and were arguing for the view of the database as a Hash Attribute Value table. Then I kept a level head and finished reading your article and you've got some very valid points. One of the greatest frustrations during development is having to change the database and migrate columns from one table to another or to re-engineer a complex relationship between tables during heavy development.

During development, managing these things is painful, especially if each developer is developing against a local copy of the database. You can even end up with two incompatible change scripts to the database.

My only concern with the approach that you outlined is that it doesn't immediately allow for including business rules in the database which is (according to some) where these types of things belong (for a variety of reasons that go beyond this comment).

On the whole, very sane and thought out argument. Even as a database developer I found myself agreeing with you by the end. It's important to have a stable product before attempting to create a well thought out and properly optimized database.
Gravatar
browniepoints - Thursday, November 06, 2008 - So I saw something that made me come back to this post...I think there's a lot of congruence between what you're discussing and what is being said in these two posts.
http://seesharper.wordpress.com/2008/11/05/the-...
http://seesharper.wordpress.com/2008/11/06/the-...
Gravatar
Dave Neeley - Friday, November 07, 2008 - The Gallio/mbUnit project has included a db40 implemenation in their 3.0.4 release. Their tentatively calling in Gallio.Ambience. You can read about it here: http://blog.bits-in-motion.com/2008/10/announci...
Gravatar
Kevin Babcock - Saturday, November 15, 2008 - Best blog post I've read in a long time! I can't wait to try this out on a new (small) project I was hoping to knock out this weekend.
Gravatar
pmlarocque - Sunday, November 16, 2008 - 1- Biggest downside for me is that doing thing the agile way, I need working software at the end of every iteration. Yes you can argue that a system with a OODBs backend is working, but most customer wont like it.

2- And I am currently taking over a project done in this fashion, ( but in memory Reposity with fillers to make necessary data avail) and the Model for the most complex part is just made in a way that you cannot translate it to a RDMS mapping.

My first point is my biggest concern, the second is mainly due to inexperience developping with this approach, but it would be for sure a pitfall to watch.

Great post!
Gravatar
robconery - Sunday, November 16, 2008 - The customer didn't like the OODB? That's agile? Hmmmm

Also I think it's weird that you can't map a model to an RDBMS.

Sent from my phone. Please excuse brief replies.
Gravatar
pmlarocque - Sunday, November 16, 2008 - Well from a development point of view using OODB and the approach you describe is indeed agile. But from a customer point of view, let say you sold with agile advantage like the possibility to stop a project at the end of any iteration and get working software up to the user stories you have done. Well if they expect a MS SQL backend, stop the project in a random iteration. Well they wont get the working software they expect. Ok I admit this case is more the exception than the rule.

For the Model I could not map to an RDBS, well persisting data to a OODB is so simple, they persisted a part that does not belong to persistence but should be computed on demand, or you end up with redundant data across your database. But now swicthing repository involve a lot of work being able to reconstuct complex object from other entities, this should have been spot sooner.
Gravatar
tobinharris - Sunday, November 16, 2008 - Great post. I think you've just drummed up quite a lot of interest in DB40 too. Like many other folks here, I've been "watching" it for years but never had a stab at using it. Cheers for the helper class too!...

As for the idea. I like the fact that it reduces friction up-front.

I don't like the fact that it creates a debt to be paid off downstream. That is, when the time comes to switch to RDBMS. Or, if you try to make repositories interchangeable to ease that pain, it then feels like you're *adding* friction up-front, just to prevent having to do more work later.

I feel like the happy ending here would simply be not to spend time switching to a RDBMS. Why not just use db40 in production? If someone wants a RDBMS in the picture, write ETL scripts to export to a warehouse :) Their dRS replication system seems designed for just that task.
Gravatar
hodzanassredin - Sunday, November 16, 2008 - Hi. Thanks a lot for Storefront MVC and sory my english :-) I I'm trying to build some repository and filters for my application like your did it in Storefront. But i have big problem: _rep.GetCategories().WithId(someId) invokes select * from Categories in db and after that apllying filter but this is very bad for performance and i prefer to invoke select * from Categories where Id == someId. What do you think about it? As i understand select in GetCategories creates new objects to compare with filter but we need to inject filter into query.
Gravatar
robconery - Sunday, November 16, 2008 - Please put these comments on the storefront posts. Linq doesn't do
select * so no worries there. Also delayed execution applied the
filter first, not after.
Gravatar
david_a_r_kemp - Monday, November 17, 2008 - I think this is a brilliant idea, but I'd refer you to Joel's Leaky Abstraction post (http://www.joelonsoftware.com/articles/LeakyAbs...).

In my experience, there's always a bit of give-and-take between the DB design/ORM/Object model (we're not all Ayende - and can't all convince our customers that running off of open source trunk code is a good idea), and you're likely to encounter a case where something that you can do with objects can't be done with DB4O, or you can do it with DB4O, but not your ORM, etc...

Also, your object design might have to be changed to get the performance you need. I'd hope this is a really edge case, but I had a certain project where a Many-to-One relationship ended up being stored by passing XML to SQL, and then having a trigger on an XML column. Nasty, nasty stuff, but it was sooo much quicker than the ORM's implementation of storing a Many-to-one relationship.

There are always cases where how you do something is influenced by which tools you're using to do it (compare ASP.NET with ASP.NET MVC, and compare ASP.NET MVC with Monorail), and not using the target technologies to develop against means you're going to hit problems later.

And, yes, performance is a development concern. No matter what you think.
Gravatar
robconery - Tuesday, November 18, 2008 - Hi David - There is no perfect ORM - that's for sure. NHib is probably as close as we can get to a mature relational mapper but there are still problems. I do agree there. However people will still try to do it - try to make a model that is OO and map it to a relational DB. My thought here is why not start with a known set? Start with your model (instead of your DB) and then adjust your DB. App first is the idea here.

I've always been a perf nut, but only over the last 2.5 years have I been swayed to address perf when needed. And believe it or not - 9 times out of 10 it's not that big a deal. More often perceived slowdowns are from too much client code (js libraries, etc); almost never a client call.

Perf shouldn't really be handled until you need to (or until a test id's a problem). ADO is really fast these days - and attributing bottlenecks to DB calls is really something from 4-5 years ago.
Gravatar
david_a_r_kemp - Tuesday, November 18, 2008 - "attributing bottlenecks to DB calls is really something from 4-5 years ago."

Attributing performance to DB Queries is still a big issue, especially when you're dealing with millions of rows.
Gravatar
dorian_farrimond - Thursday, November 20, 2008 - I like this idea because it's a step in the right direction. Sure it's a bit of a pain to implement the RDBMS at the end but like you say at least the app is developed with a pure OO mindset and relational issues don't creep into the object model.

By doing this it should eventually drive the tools to catch up with development methods. We might get to object persistence nirvana a little bit sooner than we otherwise would.
Gravatar
Jason - Friday, November 28, 2008 - I tried experimenting with this idea, but am roadblocked with trying to use System.Transaction.TransactionScope... (Not all that familiar w/ the repository pattern yet, but was hoping to use db4o to start practicing some of these techniques)... Any thoughts on how to get TransactionScope to work seamless with DB4O?
Gravatar
robconery - Saturday, November 29, 2008 - Wrap the call in using(System.Transaction)? Some details might help...
Gravatar
Jason - Saturday, November 29, 2008 - I'm not sure what you mean by "using(System.Transaction)" since that doesn't seem to exist, and System.Transaction[s] is a namespace.

There are a couple links that look like feature requests on the Db4o site

http://tracker.db4o.com/browse/COR-1376
http://tracker.db4o.com/browse/COR-1143

related to TransactionScope.

Below is the unit test I would like to figure out how to make pass.

[Test]
public void CanRollbackSavedObjectInRepository()
{
var newObject = new TestObject()
{
SomeInteger = 1,
SomeString = "string here",
};

using (TransactionScope scope = new TransactionScope())
{
_repository.Save(newObject);

if (_repository.GetAll().Count() != 1)
Assert.Fail("Singe item was not saved to the object database. itemCount = " + _repository.GetAll().Count().ToString());

//scope.Complete();
}

if (_repository.GetAll().Count() > 0)
Assert.Fail("TransactionScope Failed");
}

Any ideas?
Gravatar
Jason - Saturday, November 29, 2008 - I think I'm making some headway...

I found a project at http://code.google.com/p/uniframework/

In this project there's an implementation of IEnlistmentNotification called Db4oEnlist and in my repository I can do something like this...

public void Save(T item)
{
Db4oEnlist enlist = new Db4oEnlist(this.Container, item);
bool inTransaction = Enlist(enlist);
this.Container.Store(item);
if (!inTransaction)
this.Container.Commit();
}

the Enlist method looks like...

private bool Enlist(Db4oEnlist enlist)
{
System.Transactions.Transaction currentTx = System.Transactions.Transaction.Current;
if (currentTx != null)
{
currentTx.EnlistVolatile(enlist, EnlistmentOptions.None);
return true;
}
return false;
}

Unless there's a better way out there to do this using TransactionScope... I was able to get my unit test above to pass...
Gravatar
robconery - Saturday, November 29, 2008 - using(System.Transaction) was shorthand - I'm going to guess you know what I
mean :). Are you saying that wrapping DB4O calls in a transaction scope
won't work?
That said, I know DB40 will support them as well.
Gravatar
Jason - Saturday, November 29, 2008 - Yes i'm saying wrapping DB4O calls in TransactionScope won't work... Unless you do the work I put above (basically implementing the goo by yourself)..

not sure what you mean by "I know DB40 will support them as well."
Gravatar
mike - Wednesday, December 03, 2008 - What's really cool is this:
from Product p in DB.Container

instead of this
from Product p in context.Products

So, the products are just in the container, no indirection needed.

I will test it out for sure. It might be a goo option for a single user application instead og SQLite+NHibernate, like you said, stop worrying about all that.
Gravatar
MortMan79 - Sunday, December 07, 2008 - To try out different ORM/data access approaches I decided to implement your IRepository<T> for a small application. My idea was to implement Db4o then Linq2SQL then SubSonic 3 and see which I liked the best. So far I managed to refactor the existing data access to use IRepository. The existing data access uses Oracle, simple selects/SPs to retrieve and update data from two tables. So far I have implemented my Db4oRepository and I have encountered a fewproblems:

1. Db4o does not take kindly to multiple web sites sharing a database file. This is per design but is really a show stopper since my application consists of two distinct websites operating on the same data. Also Db4o seems to get confused saving/loading complex hierarchies of references. Loading a list (100 elements) that references a set of elements which each reference each other through a tree took 10 seconds to load.

2. It could be that I am too used to SQL, but all my objects have one or more IDs that are either their own ID or a reference. These IDs are normally assigned by the database, but Db4o does not assign IDs. So to use both SQL and Db4o I need to either make Db4o assign IDs like SQL and thus know about which field is an ID or I have to hide the IDs. Neither solution is attractive - making Db4o assign IDs seems the safest.

3. The next problem I encountered was that my Db4o repository only needs to work on one type while my Oracle repository needs to work on two types (remember I have 2 tables). This is because Db4o stores a whole object including references and references references. This is a cool feature, but SQL works differently and really provides a model that is very different.


My conclusion is that your idea is sound, especially for a quick spike which needs some persistence. Db4o might also be useful for the initial stages of a website, but once you decide to switch to SQL then you will need to make several changes and you can’t go back. I would not suggest trying to run a SQLRepository side by side with a Db4oRepository. The models are too different and the result feels wrong.
Gravatar
Chris - Monday, December 08, 2008 - love the article i've also implemented it in a sample app that i developed just for giggles... i just have a question... it does provide persistance, however in my BDD drive out session for the repository i actually found that i drove out a static list repository. obviously the static repository won't last as long as an oodb it still made for an easier way of driving and testing db persistance without having to worry about persistence.
Gravatar
Fregas - Tuesday, February 03, 2009 - The problem I have with DB40 is their licensing model. Its complicated and expensive for a lone developer. But until we lone developers can use it for our side projects and stuff, we won't be able to convince big companies to ditch the RDBMS for everything and use this crazy object stuff.
Gravatar
Bruno - Thursday, February 12, 2009 - My concern has always been Reporting. How do you run reports from a OODB? Its not like you can connect Crystal Reports or Reporting Services to it... right?
Gravatar
robconery - Thursday, February 12, 2009 - You dump your reporting stuff into a SQL Db :)
Gravatar
Slackshot - Friday, October 23, 2009 - Hm. I took a look at your recommendation of Db4oObjects, and it works out pretty nice.

Wanted to give you a thumbs up.