Flexible data models (by )

The simplest example is restricting how many of some object there can be in a situation. This usually starts with a client specifying that, in the application, every Customer has an address. So we dutifully put address columns in the Customer table in our SQL, and whenever we want to know where to send something for a customer, we look up the address from their customer record; and we put address fields in the 'edit my details' form so the customer can update them. And we add the address columns to the Order table, too, with code so that when an order is placed, the address is copied from the Customer, so if they change their address after placing an order there's never any doubt as to where orders in progress will be delivered - always to the address that was confirmed when the order was placed. Job done.

Then the client decides they want the customer to have separate billing and delivery addresses. Ok; we can put a second set of address columns in the Customer and Order tables, and change the code that creates an order since it has more address fields to copy across, and change the billing code to access the second set while leaving the delivery code be, and add a second set of address fields in the 'edit my details' form. Oh, and alter the signup process forms to accept the new address and store them correctly. At least we're not using one of those crummy object-relational mappers that makes you explicitly set up all the field mappings, rather than the nice one in Django that does it for you (at the cost of some performance) or just direct SQL access.

Ok, a few hour's rather laborious work doing it and testing it.

And then one day the client decides they'd like something like Amazon has, where you have an entire address book with an arbitrary number of addresses, one of which can be nominated as the default billing address and one as the default delivery address, and when you place an order you can pick any address in either role if you don't like the defaults.

Suddenly, we need an Addresses table with a customer ID and an address ID, along with the address fields, and in the Customer table, a defaultBillingAddressID column and a defaultDeliveryAddressID column, and in the Order table, billingAddressID and deliveryAddressID. Whoops, no - we'd best still copy the entire address in, in case the user edits the address after placing the order. And now we have to change the signup process, the order process, write a separate address edit page and change the customer account edit page to have a list of addresses with add/remove buttons and edit links to the edit page. Tiresome.

And if you used addresses for more than just billing and delivery, perhaps as an integral part of your application itself (find people who live near you?), then you'd have a lot more work.

Now, personally, I'd have been keen to base the data model around there being any number of addresses to begin with. Recognising, with experience and intuition, that there might be multiple addresses associated with a customer, making the table structure that way in the first place. If the client swears blind they just want one address per customer, then fine - collect one address at signup, make a single address record with the appropriate customer ID, but in preparation for having multiple, have defaultBillingAddressID and defaultDeliveryAddressID columns in the Customer table, but have no way of changing them; they just point at the singleton record. If you feel like it, have two sets of addresses in Order for billing and delivery and set them both up from the default billing and delivery addresses referred to from the Customer - if you want to be really future-proof - or, for now, just have a single address that you copy from the address record referenced by defaultDeliveryAddressID, knowing it won't be too hard to change later (that's a decision whose correct answer will vary depending on tiny details, to be honest). And see if the client fancies having a separate page to edit the customer's address, in which case it can be passed the address ID as a URL param, so can be used in future to edit any address from a list, although for now we just have an Edit Address tab on the edit-my-details page that links to it.

In other words, where it's not too much of a burden, code for the more general case up front, in case the client wants to expand things in future; a bit more work coding it open-mindedly to begin with is often easier than rewriting lots of stuff in future. On the one hand, you might do extra work that's never needed; on the other hand, changing existing code can be a nightmare if it's hard to find out exactly what depends on the thing you're changing, leading to strange bugs that get noticed further down the line.

Another trick that helps is putting more layers of indirection in. If we use a nice object-relational mapper, then we can give our Customer and Order classes nice getBillingAddress() and getDeliveryAddress() methods. Then we can change the actual SQL structure from inline addresses to a linked table of addresses, and know that nearly all the code we need to change is contained within those two classes; we can make the Order constructor copy whatever data it needs from the Customer, and alter the getAddress() methods of both. The only changes that need to go further are changes that actually affect the user experience; when placing an order we need to have the user select their addresses from the list, so need extra parameters to specify the IDs of the addresses to use. And, actually, we need to remove the getAddress() methods of the Customer and replace them with getAddresses() that returns a list of address objects, and instead have getDefault*Address() methods, along with removeAddress() and addAddress(). And once we've made those changes, if we're using a typed language, our compiler might even tell us if we've missed any bits of code that depended on the old Customer address interface.

But I still wonder if we could do more.

Pages: 1 2 3

No Comments

No comments yet.

RSS feed for comments on this post.

Leave a comment

WordPress Themes

Creative Commons Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales
Creative Commons Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales