Skip to end of metadata
Go to start of metadata
You are viewing an old version of this page. View the current version. Compare with Current  |   View Page History

Zend Queue Refactoring

The ZendQueue is currently in a bad state. The DB implementation is based on Zend\Db from one of the previous Beta releases and it's at least not functional at all. Same with "PlatformJobQueue" - Zend Job Queue adapter implementation needs a complete rewrite. And the coding style looks very mixed - from php4 to php5, some oop code, some common interface, many strings that interpreted to cause different object generations at runtime and so on (but not on an OOP way such as factories).

The adapter's choice itself is also not really up 2 date - I'm missing at least Beanstalkd and maybe Gearman (should be discussed). Maybe you should think about removing MemcacheQ - it's not maintained anymore and shouldn't be used in an enterprise architecture anyways. But doesn't hurt anyone to keep it in there, it's simple ...

I'd love to take over the refactoring for this component. Maybe it will be the best to rewrite this component completly (but this will be of course up 2 you). If you want to, I can come up with a prototype on github that follows my suggestions below. You can review that and I'll do the "details".

Component Overview

The ZendQueue component itself is very straight forward. There are only a few logical subcomponents:

  1. Zend Queue
    The "interface to the user" - you're working most of the time on this component
  2. Message
    The message that is sent to the queue or retrieved by the queue
  3. Adapter
    The implementation itself for the specific technologies (based on an AbstractAdapter)
  4. Exception
    A big bunch of custom exceptions are delivered by this component

The following adapters are implemented:

  1. Activemq (+ Stomp)
  2. Memcacheq
  3. Null
  4. Array
  5. DB (+ AbstractTableGateways and sql's)
  6. PlatformJobQueue (has to be ZendJobQueue)

Architecture overview

ZendQueue

The class \ZendQueue\Queue has following dependencies (within this project):

  • \ZendQueue\Message
  • \ZendQueue\Message\MessageIterator
  • \ZendQueue\AdapterAbstract
  • \ZendQueue\Exception\ExceptionInterface
  • $options - an array or \Zend\Config

This component is the "interface to the user" from an API point of view. Most of the actions are performed against this kind of object.
Some methods are used to be a proxy for the adapter implementation. Some of them are only for configuration (with an array or \Zend\Config).

Adapter

The class \ZendQueue\AbstractAdapter has the following dependencies (within this project):

  • \ZendQueue\Queue
  • \ZendQueue\Adapter (internal interface for adapters)
  • \ZendQueue\Exception\ExceptionInterface
  • $options - an array or \Zend\Config

This class (together with the Adapter interface) is the base for every adapter. As you can see, there is a cross-dependency to the queue.
The whole functionality of the used technology took place in the implementations of this abstract class. Simply extend \ZendQueue\AbstractAdapter, implement the needed methods, overwrite what you need, inject to the queue and work on another queue system.

Message / MessageList

The class \ZendQueue\Message has the following dependencies (within this project):

  • \ZendQueue\Exception\ExceptionInterface
  • \ZendQueue\Queue

This class represents a message that is sent to the queue or received from the queue. Magic methods get, set, isset, sleep and wakeup are implemented.
Some helper functions to import an array or build object from an array.

The class \ZendQueue\Message\MessageIterator has the following dependencies (within this project):

  • \ZendQueue\Adapter\AdapterInterface
  • \ZendQueue\Queue
  • \ZendQueue\Message
  • \ZendQueue\Exception\ExceptionInterface

This is a Message Container that is returned by the adapter methods. All of the methods that are working with the message and the message iterator are proxied from the queue to the adapter.
So there's no explicit cross dependency between the queue and the message.

Refactoring suggestions

Some suggestions about a possible refactoring.
It's not a refactoring by definition due to interface changes - but I think we can stay on this wording

Queue and Adapter dependency

The queue is used as a proxy to the adapter by some methods:

send (ZendQueueQueue)

As I said above, there is a cross dependency between the Queue and the Adapter:

send (ZendQueueAdapterActivemq)

I see no point, why the queue at all shouldn't be used as a proxy?! If the whole queue is doing nothing else than proxying everything to the adapter, the cross dependency is gone and the queue object can still be used as a "single point of usage" for the end-user. And all the exceptions that are thrown in all of the not implemented methods can be handled at a central place:

isExists (ZendQueueQueue)

And such code like this can be removed completly from the adapter implementation:

isExists (ZendQueueAdapterActivemq)

isSupported and getCapabilities

The way how specific adapter functionalities are checked is based on a kind of configuration array. The function getCapabilities (part of \ZendQueue\Adapter\AdapterInterface) is used by isSupported from the Queue to check individual functions, if they are supported by the used adapter or not:

getCapabilities (ZendQueueAdapterActivemq)
isSupported (ZendQueueAdapterAbstractAdapter)
isSupported (ZendQueueQueue)

I suggest to either use duck typing here to check for the existence of specific methods in the adapters, or develop against feature interfaces.

Duck typing

Duck typing will be an easy way but not very object oriented. I suggest to choose this if the number of features is higher than 8 - otherwise the other suggestion will look a bit overengineered.

isSupported (ZendQueueQueue)
Feature Interfaces

If the number of features is not that high, I suggest to implement a feature interface such as:

FeatureInterface
isSupported (ZendQueueQueue)
Adapter

Note: If the queue will be changed to a simple proxy, it should implement all provided feature interfaces!

Component configuration

I suggest, to use the same way as in \Zend\Cache to configure the instances:

Become something like this in \ZendQueue:

The options array will be the configuration that is passed to something similar than \Zend\Cache\Storage\Adapter\AdapterOptions.
Therefor, you can have a major one for the standard config and an apdapter specific configuration can easily be implemented for the delivered ones and even for custom adapters.

Component Strategy

When I'm looking now on the featureset, that is provided by ZendJobQueue and on activemq, the functionality is completly different. You simply want the "basics" in this component provided by most of the queue services or as much features as possible for different adapters? It will not be very easy to develop against interfaces, if the functionality is not even compareable.

Version support

What is your strategy if such components rely on specific versions of a service? Memcacheq changed their resultset for "stat" from 0.1.* to 0.2 - it was not compatible anymore. Also activemq switched to a never version of the stomp standard a few years ago. A kind of "VersionFactory" can be easily implemented to have a MemcacheQ Adapter for 0.1 and one for 0.2. Same with activemq - simply the Stomp implementation can be changed. You need that or are you fine with simply having it up 2 date?

ZF2 integration

If you want to, I can also come up with a suggestion to integrate this component in ZF2. I only need a bit background about how you'd like to proceed with this component (I think queueing is very common in an enterprise architecture).

Rewrite of ZendQueue

The ZendQueue itself has a history for about a few years. You can really see that in the code. Maybe it might be the best to rewrite this component from scratch. Even the sourcecode on the implementation itself looks a bit "old school" to me - definitvly not the way you do it in ZF2. It might be a good idea to change coding style, interface implementations, architecture ... to fit more the way you do it in zf2.

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.