Freefall: conceptual modeling and the Object-Relational Mapping Diagram (ORMD)
A long time has passed since my last post about Freefall, where I quickly explained the major steps I took for Requirements Analysis.
Let me first say that the project is almost complete and the reason why I chose not to update the series further until now is because the following step (development of an Object-Relational Mapping Diagram) was a task I really consider complete only today. In fact, when you develop such a complex diagram, it is quite impossible to think of all the attributes you’ll need so early in the development process. More likely, the game you’ll play would be that of first define in the conceptual model a basic set of attributes you’re sure you’ll need, then develop the logical model based on the attributes you identified in the conceptual modeling. From this point on, you’d probably tune each of the two forementioned models based on the discoveries you’ll make in any of the two, e.g. if you find at any point that in order to implement a given functionality you need an attribute you did not include in your conceptual (ER) diagram, you’d first add this attribute in a table definition, and then include it in the diagram too. On the other hand, you may come up with a better arrangement of the attributes in your conceptual model, and this will of course reflect in the respective changes in your logical model.
All this procedure has become very stable in Freefall, so I can now show you the ORM Diagram.
The Object-Relational Mapping Diagram (ORMD)
I chose not to develop neither an Entity-Relationship Diagram nor a Class Diagram to design the system, but an Object-Relational Mapping Diagram instead. Why? The final ORMD shown below is a refinement of a first, very rough ER Diagram consisting of only the Entities discovered in the Requirements Analysis. ERs are very useful tools for quickly and easily focus on initial ideas, when they’re still few and blurry. But as you develop the system more and more they soon become not much suitable, as they cannot represent composite values or entities relationships such as composition and aggregation. Not to mention inheritance, which is not supported by pure relational modelling. These limits are overcome with Class Diagrams; so, instead of simply creating two separate diagrams (an ER Diagram and a Class Diagram), I created an Object-Relational Mapping Diagram, which I used to map Objects (expressed in UML Classes) to Relations (expressed in ER Entities), as the name implies.
Now, it’s worth saying that this is not the intended scope of Object Relational Mapping techniques, but I took the freedom to borrow the ORM Diagram tool in order to simply refine that initial, rough ER Diagram into something more expressive and powerful.
The orange boxes are entities, as they’ve been identified in the Requirements Analysis phase. Note as they are all and only the ones early actually identified. The blue boxes are classes. In total, we have 9 entities and 19 classes. The reason why we have more classes than entities (as I explained above) is an effect of the refinement: in fact, I first created as much classes as the entities were, and then started wondering how to extend the design. Let’s discuss these extensions in detail.
Discussing the extensions in the design
The first kind of refinement is regarding how to model addresses. Right from the beginning I tried to look forward and decided to give the possibility to potentially use any existing addresses in the world, thus promoting extensibility. So I created two classes: a parent class named Address, which contains information related to the address’ state and city, and a child class named ItalianAddress, which also adds attributes to represent the street name, the street number and the ZIP code. The State will be of type States_e, where the trailing ‘_e‘ stands for enum, which we’ll discuss in a later post. ZIP codes are not treated as enums because when the development started (October 2012), italian districts reorganization was in place, so it had no much sense to create enums as it was not (and maybe still is not) a stable concept. It’s clear that a customer can have one or more addresses, but we’re not interested in knowing all the addresses owned by a given customer, so we simply ask him for one, the main one. This way, in the Freefall little world, each customer has one and only one address and that’s the reason why the relationship’s multiplicity between the Customer and Address classes is a 1-to-1 aggregation relationship – where the whole is the Customer class and the part is the Address class, since in no way if a Customer object is deleted (or even killed!) an Address object’s lifetime terminates. We have to decide which object must refer to the other. In that it is a 1-to-1 relationship, any of the two objects can do this, but the common sense suggests us to let the Customer class refer to the Address class. Another reason for doing so is that the Customer is a central entity in our domain, much more than what the Address is meant to be, so it is more likely to ask for the address given the customer, rather than the contrary. Finally, The Address class is an abstract class, since in no way we can instantiate generic addresses if we don’t know what their structure is. We will use rules to prevent the Address class (resp. table) from being created (resp. populated) or manipulated:
CREATE RULE no_insert_in_address_table AS ON INSERT TO Address DO INSTEAD SELECT operation_not_allowed_FNC('INSERT', 'Address');
where the operation_not_allowed_FNC(text, text) function simply raises an exception each time someone tries to manipulate the table whose name is passed as the second argument:
ERROR: Operation not allowed: INSERT INTO Address.
The two parameters are simply used to personalize the exception message. Other rules are created for UPDATEs and DELETEs, so each of the 5 abstract classes appearing in our ORM Diagram (Address, TelephoneNumber, Operation, OrdinaryPlan and BankAccount) will have 3 rules, one for each event: INSERT, UPDATE and DELETE.
The next kind of refinement pertains to telephone numbers. Also in this case I used a base abstract class, named ‘TelephoneNumber‘, which holds information about country codes, treated as enums. TelephoneNumber has two subclasses: ‘CellPhoneNumber‘ and ‘HomePhoneNumber‘. The former models mobile phone numbers (to do so it holds the operator prefix and the core number), whereas the latter models home phone numbers and so it holds town prefixes and core numbers, too. The reason why they both store the core number is because not necessarily it has the same format for cell phones and home phones. CountryCodes, operator prefixes and town prefixes are declared to be of enums type. A Customer is supposed to possess no, one or more than one alternative telephone numbers, so the relationship’s multiplicity is declared to be a 1-to-many relationship. Moreover, it is an aggregation relationship, because just as in the Address case, if a Customer gest accidentally killed in a car crash his telephone numbers remain; another reason is because it would be completely a nonsense stating that a Customer “is composed by” TelephoneNumbers. Since a Customer can potentially own more than one telephone number, we use a container to hold them in the Customer table. Note: the best choice here is to store TelephoneNumbers and not separated CellPhoneNumbers or HomePhoneNumbers, since we have no mean to know in advance what kind of phone numbers Customers will already own. An identical reasoning applies to the Operation table, since we want to register operations towards both type of phone numbers, while in the Contract table we’re most likely to include only CellPhoneNumbers, of course.
Last kind of refinement is related to the modelling of bank accounts, which are of interest for licensed customers only (customers owning a PayMonthlyPricePlan). Again in this case I created an abstract base class, named BankAccount, which only stores information about the balance, and a derived class, ItalianBankAccount which models an italian bank account using the italian version of the International Bank Account Number (IBAN) and storing information about the currency code (‘EUR’ for Italy). Just like addresses, I made the system suitable for future developments giving the possibility to use other kinds of Bank Accounts if required.
And that’s it for now. In the next post we will translate the conceptual model seen today into a logical model and we will talk about inheritance, so don’t miss it. See you! :)