-- Serkan Temizel <[email protected]> wrote
(on Wednesday, 23 February 2011, 06:09 PM +0200):
> After a question about sending mail from controller or model (it is
> here<http://zend-framework-community.634137.n4.nabble.com/send-mail-notices-from-the-controller-or-model-td3319085.html>*)
> I got some unexpected answers. Jurian mentioned  about a service approach.
> After this post I made some search about services and  found this
> article<http://www.angryobjects.com/2009/03/30/writing-robust-php-backends-with-zend-framework/>**
> 
> Leendert Brouwer's article was on something totally new for me. Because it
> was adding a new layer to MVC, a service layer. domain objects and mappers
> are also new to me.
> 
> I searched the web for documents about this new concept but I found nearly
> no helpful resource.
> 
> I need some valuable resource and coding and structuring samples about this
> approach.

Martin Fowler's "Patterns of Enterprise Application Architecture" has a
section on Service Layers. I've also spoken about them a number of times
in several presentations I've done (http://slideshare.net/weierophinney).

Basically, think of the Service Layer object as the public API of your
application or resource: these are the things you can do.

As a quick pseudo-code example:

    class BlogResource
    {
        public function create($data)
        public function fetch($id)
        public function update($id, $data)
        public function delete($id)
        public function fetchRecent()
        public function fetchByYear($year)
        public function fetchByMonth($month, $year)
        public function fetchByDay($day, $month, $year)
        public function fetchByTag($tag)
    }

I typically also compose something like the new ZF2 EventManager or a
pubsub implementation of some sort, which allows me to tie into specific
actions in order to introduce such things as ACL checks, etc. The
service object then takes care of interacting with my various domain
entities, mappers, etc.

Your MVC application then is simply passing input to the service object,
and handing the response off to the presentation layer. As an example:

    class BlogController extends Zend_Rest_Controller
    {
        public function init()
        {
            $this->resource = new BlogResource();
            // optionally configure this: inject the data access layer
            // and/or mapper, tie in event handlers, etc.
        }

        public function indexAction()
        {
            $this->view->entries = $this->resource->fetchRecent();
        }

        public function getAction()
        {
            if (!$id = $this->getRequest()->getQuery('id', false)) {
                throw new DomainException('Missing identifier');
            }
            $this->view->entry = $this->resource->fetch($id);
        }

        public function postAction()
        {
            if (!$data = $this->getRequest()->getPost('entry', false)) {
                throw new DomainException('Missing data; cannot create entry');
            }
            $this->view->entry = $this->resource->create($data);
        }

        public function putAction()
        {
            if (!$id = $this->getRequest()->getQuery('id', false)) {
                throw new DomainException('Missing identifier; cannot update');
            }
            if (!$data = $this->getRequest()->getPost('entry', false)) {
                throw new DomainException('Missing data; cannot update entry');
            }
            $this->view->entry = $this->resource->update($id, $data);
        }

        public function deleteAction()
        {
            if (!$id = $this->getRequest()->getQuery('id', false)) {
                throw new DomainException('Missing identifier; cannot delete');
            }
            $this->view->status = $this->resource->delete($id);
        }

        public function fetchByYearAction()
        {
            $year = $this->getQuery('year', date('Y'));
            $this->view->entries = $this->resource->fetchByYear($year);
        }

        // etc...
    }

You'll notice that the individual action methods are succinct, and
simply proxy to the service object, after first marshalling the input.

Later, you can hook the same service object up to one of the server
classes:

    $json = new Zend_Json_Server();
    $json->setClass($blogResource, 'blog');
    $response = $json->handle();
    echo $response;

Basically, you're writing a re-usable application layer that simply
needs something to provide input, and another layer to produce the
presentation.

> More importantly I need your opinions about this approach. Why ZF
> don't ship with a service layer or default mapper? Are they really
> needed or they just increase the complexity?

We don't ship a mapper because, frankly, ORM solutions are the sort of
thing that need entire teams to get right, and there's a great solution
out there already: Doctrine 2. 

Regarding a service layer, we can't really ship a service layer by
default -- service layers are highly application-specific (as is the
entire domain model). About the most we could likely ship is an
interface for RESTful resources, and a "base controller" for RESTful
resources that expects to instantiate a RESTful resource and which would
provide the basic functionality as outlined in the controller above
(this would be for basic CRUD operations only). I'm definitely
considering this for ZF2, as it would streamline many common workflows
-- but you'd still be responsible for writing the various domain
entities, wiring in an ORM (if used), etc.

-- 
Matthew Weier O'Phinney
Project Lead            | [email protected]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc

Reply via email to