The Pope Is Dead ::

Main Navigation

The pope is dead. Long live the pope! Just another blog about web development. If you’re interested in web stuff you may find this blog useful.

blog latest entries

Moving On From The Storefront Model Design

Its coming up to nearly two years since my book was released, so I thought it was about time I did some blogging again.

Over the past couple of years I have spoken to various people who have read my book and many have liked the way I implemented my Models for the Storefront, obviously looking at the design now I can see many design mistakes, some of which I did actually mention in the book. A lot of people also want to use my design in their applications and they occasionally email/tweet me to ask things, what I usually end up saying to them is the design was very focused on being an example for the book and a such its probably not what I would consider production ready code, I then normally advise them to checkout Doctrine 2 and consider splitting read and write so the Models become write only (more on this later).

With this in mind I have decided to go back and refactor part of the Storefront model, to start we can look at some of the guides we can use to improve the design.

Modelling Behaviour

One of the most important things that is missed by many examples on Zend Framework models and Doctrine is that the models usually contain little or no behaviour. When we have models like this we are usually looking at the Anemic Domain Model anti-pattern. Whilst this probably does not apply so greatly if we are not using the Domain Model pattern we can still learn some valuable lessons from it. By modelling behaviour we should end up with a simpler and more lean model, this also helps when we come change the model as each behaviour is distinct.

We can identify models that do not implement behaviour fairly easily by looking for the following factors:

  • Lots of Getters & Setters
  • Model methods have no "Verbs" in them
  • Behaviour is found somewhere else, the model is used or wrapped to provide behaviour
  • Unit tests test the data structure of the model

Tell Don't Ask

Tell Don't Ask is a style of Object Oriented Programming that states that an object should only ever tell another object to do something, it should never ask an object for its state. The reason for this is nicely described by Alec Sharp in Smalltalk by Example where he states:

"Procedural code gets information then makes decisions. Object-oriented code tells objects to do things."

So by using Tell Don't Ask it forces us to "do things" and means we have no getters or setters in our model. This may seem like a strange concept to start, as I know you are probably wondering how we display information which I will cover shortly. The main advantages here are going to be that we have a truly OO model that has proper encapsulation, it also means that we don't have lots of other code that makes decisions based on the state of an object. Not using Tell Don't Ask, we usually end up with a rich service layer that contains the actual behaviour, this then means we have models that only contain data and getters/setters, and this in turn leads us again to an Anemic Domain Model.

Separating Read & Write

Another thing that can cause us problems is mixing the Domain objects with the View (Read). If we do this we generally end up compromising our model to support various display problems, we can also get long object chains like $user->getClient()->getAddress()->getPostCode(). This will also lead us to violate encapsulation and forces us to mix display and behaviour, by doing this we cannot adequately address either problem. Also in PHP we have problems with its share nothing architecture, unlike some other languages (Java, .NET) PHP cannot store object beyond a request, this means that we have to reload our objects every request, whereas other languages can use object between multiple requests. This is not a fault within the PHP language just a fact that we need to be aware of when dealing with complex object models, indeed in other languages you have to think about locking etc much more so there is a payoff either way.

We also need to consider the use of an ORM (like Doctrine 2) here, now ORM's provide us with an elegant toolset for persisting the state of our objects, however they do not answer all our problems! When using an ORM you will undoubtably at some point need to display a few thousand/million "things" at the same time, say for an export of orders. Now what we find here is that we cannot use objects as we know we will run out of memory, therefore we end up side stepping the ORM, we choose to use things like Native Queries (Doctrine) or create a service that gets the data straight from the database. This is all very valid but if we didn't mix display and behaviour we wouldn't get this problem.

Database design is also key here, just like when the design our model, the database is designed to serve a particular task, in the case of our models this task is to persist the state of our domain. Now ask yourself the questions is this the same problem as pulling reports from the database or displaying information to the user? I would say probably not, to display a report for example we may need summary fields or calculated totals for performance, we cannot create a one size fits all database, especially if we need high performance.

Refactoring the Storefront Catalog

Right now time for some code, in this section we will go through and refactor a small part of the Storefront model and try to apply the guides above to it. For this we will keep the model really simple so we don't get over involved in the implementation, so our basic spec is that we can create products and products have a name, category and price, we can also update the product name.

You can find the code for this here: https://github.com/muteor/Examples/tree/storefrontModelPart1

First a test…..

Here we have the test-case for our basic product model which contains two tests. The first test is pretty normal and simply tests the creation of a new Product, notice that we have no getters here, this is why we are using PHPUnit's readAttribute method to read the protected properties. Another notable aspect is that we are using the constructor in the way it is meant, we intend to not have any base class that implements the construct, this means that whenever we do new Product we will be creating a brand new Product. We also use the constructor to validate our object, by using the language feature we can make sure that a new product is valid by adding the requirements into the constructor (its parameters).

The second test, tests the process of updating the products name, here we see that to do the update we do not use a setter. Instead we introduce the concept of a Command, a Command is applied to a model and the model processes it to complete the state modification. By doing this we are now only communicating to our domain in objects, this means we have a high level of encapsulation and separation.

The idea to use Commands is partly because we want a Tell Don't Ask style system, however this is also related to the Command-Query-Separation (CQS) pattern, this states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. This gives us clear markers to what methods make state changes and which ones don't, though in our models we are not going to have anything that returns state.

If we now look at the our model classes:

Looking at the Product construct we see that it takes three parameters, these are what we deem to create a valid Product. In a real world situation we would probably have many more properties that are required to make a valid Product, in this case we can sometimes find we have long parameter lists, to mitigate this consider using Parameter Objects. If we look at the body of the constructor we see that it creates the CreateProduct Command and applies it to the object via handle(). Now the new call in the constructor would normally ring alarm bells for me as new is always problematic for testing, however in this case I treat the Commands as an integral part of the model and test these along with the model, this is essentially because a Command should be a very simple DTO that we can control easily in our tests. The handle() method simply takes the class name of the Command and looks the corresponding handler, so for createProduct this is the onCreateProduct() method.

The onCreateProduct() method takes the command and applies the data contained in the command to the Product, all very simply really, though notice we use a protected method so everything is applied via handle(). Similarly in the OnUpdateName() method the handler simply takes the command data and applies it to the Product, obviously these are fairly simple examples we could have validation etc in our handlers that would throw various exceptions for us.

Finally we should look at the Commands, they should hopefully be pretty self explanatory.

Looking at the AbstractCommand class we see that it uses the PHP magic __get() method, we use this to enforce a read-only object so that the Command cannot be changed once it has been created. Both the the concrete Command implementations simply extend the AbstractCommand and define the protected properties that make up the Command, all properties are passed in the construct of the Command.

Wrapping up we can see we have a simple design and nice separation of concerns going on IMHO. Now working like this can be kept simple though if we face complex domains we can also consider using Domain Driven Design (DDD), DDD provides us with a few more tools to guide us in our modelling practices. I won't go into too much detail but if you are interested the two most important ones when working like this are Bounded Context and Aggregate Roots, though also checkout Entities and Value Objects.

Persistance

Once we have our Domain modelled (and tested) we need to persist its state, for this I would suggest using Doctrine 2, it is in my opinion a great ORM for PHP. I will be rewriting the Storefront example using it when I get some time (and when ZF2 is more stable), so you will be able to see some solid examples then, until that time the Doctrine documentation and examples online should get you started.

Creating a Read Model

So far we have no way of reading state from our models, as we have no getters. We could simply add these and use our models in the view, however I have found that this becomes heavy and has the potential to hinder performance, therefore we can create a Read Model. A Read Model can be as simple or complex as you like what we want to do though is balance our needs for performance and maintainability. We could simply have service classes that contain a range of methods that query the database directly or we could go a little more complex and implement some sort of service that returns simple DTO's of our objects.

What I have been using is Doctrine's query interface and instead of returning the mapped entities returning array or scalar values. Using this I get all the relationships handled nicely by Doctrine making it really easy to query my Domain. In cases where this is not performant I use Native Queries or use the DBAL directly.

Here is my base Read Model class:

The base ReadModel class simply wraps the doctrine query interface and provides as set of convenience methods for returning the queried data in various formats. We can then extend this class to implement domain specific querying, for example we would probably have a Product Read Model that would return products by categories etc. This could look something like this:

Going Even Further

Time to come clean, what I have been going through here is an ultra simplified version of a CQRS system by using some of the core concepts behind it. CQRS or Command-Query-Responsibility-Segregation is an architectural pattern/methodology that can be used for highly scalable distributed systems. The principles behind it are fairly simple though the implementation can be complex. If you would like to know more I would suggest starting here, this article gives a great introduction. Obviously CQRS is a very high level pattern and most of the time we do not need the type of performance etc that it provides, but I believe we can learn some valuable lessons from the concepts that drive it, also by using what I have shown here you also have the option of refactoring into a more CQRS like system in the future.

Wrap Up

So this article represents my current view on modelling in PHP, this is all pretty much the bleeding edge of my knowledge so I would be interested in hearing views on this approach and if I am missing anything major. So far I have had much success with using this approach, I find it keeps things simple as well as providing chances to scale the application further in the future. You do end up with a lot of Commands, though this does help when creating API's as everything is very discrete, also you can do nice things like queuing commands offline and applying them later which is handy for occasionally connected clients.

Thats all for now, enjoy!

 

Posted on 13/11/2011 at 01:40 PM

New Zend_Application feature/update

Just noticed this in the trunk of ZF, probably in 1.10 too. You do not now need to register the default resource autoloader for your default module, you can now simply put appnamespace = “myNamepase” in the app config. This will register the default resources for your default module.

Posted on 22/12/2009 at 02:59 PM

New Storefront Example Additions

I have added search functionality to the Storefront example using Zend_Search_Lucene from the Zend Framework 1.8: Web Application Development book, I will hopefully get some time to write about this over christmas, for now though if you are interested you can download the code for the Google code sites trunk.

ZF Storefront Example on Google Code

svn checkout http://zendframeworkstorefront.googlecode.com/svn/trunk/ zendframeworkstorefront-read-only

Enjoy...

Posted on 13/12/2009 at 02:26 PM

Previous Entries  

about me

I am a web developer/ project manager from Birmingham Uk, currently I am working for inflight productions as a technical project manager. Hopefully I will be posting here as regularly as possible on my experiences with various technologies that I am using and maybe the odd random thing..

the book