Separating models and logic for storing and loading
In this blogpost I will showcase a simplified problem I recently encountered. Basically I am wondering how storing data and retrieving data are different and how you should / could model this. Since we need different information while storing and retrieving information it makes sense to model those actions in different ways. The how is still bothering me and in this post I give a possible solution. I truly hope I can get some reactions and thoughs of other people in on how they would solve these kind of problems.
Note
A lot of code below is hugely simplified to keep this post under the size of your average book.
The context
My project exists of over 50 modules doing stuff like CRM, CMS, Meetings, Document management (Library), etc. These modules are reasonably well seperated from each other with API's and repositories. Still a Document is uploaded to the Library system by a Relation from the CRM. This would look like so:
$DocumentApi->saveDocument( Document $Document, Relation $Relation );
On some level in some repository this would lead to such a query:
INSERT INTO `documents` SET `document_id` = '{ generated unique ID }', `document_location` = '{ Some filepath }', `document_owner` = '{ The ID of Relation }'
The Problem
I'm adding messages to the concept of meetings. A message has:
- a title
- some content
- zero or more linked documents (Documents from Library, stored as id's)
- one or more Recipients (Relations from CRM, stored as id's)
- a Sender (Relation from CRM, stored as id's)
When storing my MeetingMessage I can make do with exactly the information described as above. I do not need Document objects and Relation objects. Their id's are enough (and what I get from formelements anyway). When I load a MeetingMessage however, I need different information, not just Document id's.
I'm most likely not even interested in a Document id. I will need a title and a download link so I can list and link documents and probably not much more. I will certainly not need the full Document object from the Document Management system. Those have versioning and a lot more that we do not want to expose to a random frontend.
A solution
So I have a part of my application that is storing data in some location and another part that is loading. Both have very different requirements to what information should be available. The solution below is one of my first tries at modelling this. First have a look at the set of Models used for retrieval:
class MeetingMessage { public function __construct( $id, $title, array $MeetingMessageDocuments //etc.. ) { //Initialise fields... } public function getId() { return $this->id; } public function getTitle() { return $this->title; } public function getMessageDocuments() { return $this->documents; } public function getMessageRecipients() { return $this->documents; } } class MeetingMessageDocument { public function __construct($title, $url) { //Initialise fields... } public function getTitle() { return $this->title; } public function getUrl() { return $this->url; } } class MeetingMessageRecipient { public function __construct($name) { //Initialise fields... } public function getName() { return $this->name; } }
As you can see these are simple straightforward objects. The only object that has an id in this case is the MeetingMessage itself. MeetingMessage consists of simple value objects or fields of basic types as string or integer. This part is pretty much satisfying to me. I expose exactly what I think needs exposing. No Relation id's or Document versions, etc.
There are several ways of creating these "dumb" objects from the full fledged Relation and Document ones. Here I chose to ask their respective API's to supply me with the data exactly as I needed it. A second option would be some sort of adapter where MeetingMessageRecipient would for instance receive a Relation in its constructor and then trough setters expose only those functions of Relation that you deem necessary.
I am actually pretty happy with this part of the solution. The problem is the part of storing data. I currently use a very simple object, as you can see it hardly deserves the name:
class MeetingMessage { /** @var string */ public $title; /** @var array */ public $documentIds; // etc... }
This is very different. I am not particularly happy with it, but on the other hand it fits my needs perfectly. What do I feel is wrong with this:
- publicly accessible fields. But for instance an ID is not mandatory. An alternative would be a constructor with optional values but that's as lame as this.
- It gives me the idea of being a glorified array with completion in my IDE
- I do not know what to call this exactly (which is somewhat of a smell)
So what do I like about it:
- It does what I need and exactly that. No heavy loading of all kind of objects but simply exactly what is going into my database.
That's it really :)
In conclusion
I like the loading side of this solution. I like the clear distinction between loading and storing. I also like how this could scale to different sets of objects for loading in different views. This works towards storing data in multiple ways. One that maintains the normalised structure and several others that store exactly what is needed in a view. No joins, no other API's, superperformance :)
The storing however, feels a bit icky still. Maybe more than a bit since it caused me to write a post. I am very curious about your opinions and improvements so please do not hesistate to drop a comment!