PHP Service Layers: Modeling Domain Objects

In this first part of a series, I introduce you to what a service layer is and how to use it in conjunction with an MVC stack. While you don’t need to use one in every possible case, a service layer can be of great help in interfacing sections of an application with multiple client layers.

While an MVC stack can be implemented in several different ways (quite possibly there exists one per developer), you know that a minimalist approach goes in general with the following application flow: once a request has been triggered within the user interface, the front controller does some routing and dispatches the request to the appropriate action controller, along with the associated argument(s).

In turn, the action controller grabs one or more models and uses them to perform CRUD operations in the persistence layer (usually a RDBMS, but it could be a web service or anything else). Finally, the user interface is updated accordingly by passing to the view(s) the result of the operations (i.e. a list of database records, an updated form, or whatever is suitable for that specific request). From that point on, the entire operation cycle can be restarted over and over again, according to the actions triggered by the user.

Although I’d agree that this schema is pretty efficient when implemented in a few simple use cases, unfortunately it doesn’t scale up very well. As an application grows in size and complexity, it’s necessary to take a more flexible approach. Thees include decoupling the models from the underlying storage (yes, the model = database equation is incorrect); implementing some mapping/repository layers; or even developing an independent, clean domain model, whose participants, entities and value objects, are persistence agnostic.

The scenario can become even more complicated, especially when it’s necessary to perform some additional tasks, such as pulling in data from an external API (Twitter, Facebook), or interoperating with different front-ends. Now, imagine that you need to translate all of that functionality to each action controller in the original MVC layer described above. You’d end up not only having really fat controllers, but code duplication will reach alarming levels (even if you’re a copy/paste master). And if you ever needed to migrate your application to another infrastructure or framework…well, I wouldn’t want to be in your shoes in a case like this, trust me!

So, what can be done to tackle such a situation without pulling your hair out? Well, there are a few approaches that can be taken. There’s one, however, that’s particularly engaging because of its straightforward implementation. As this article’s title suggests, I’m talking about a service layer.

A service layer is an enterprise-level pattern that permits you to encapsulate application logic behind a single interface (or a boundary, to express it a la Fowler). With this interface shielding from the outside all of the operations that are internally performed by a program, it’s really simple to create complex services that can be consumed by different front-ends or by distinct client layers (in the previous example, your action controllers) while keeping code duplication to a minimum.

Of course, the academic concepts that surround the implementation of a service layer must be backed up with concrete, functional code. Therefore, in this article series I’ll be developing a user service with PHP, step by step, so that you can grasp the pattern’s underlying logic and see how it fits into the mainstream of modern web application development.

But before I move on, a word of caution is in order here. Keep in mind that creating the mentioned service will require us to implement a domain model, some data mappers, collections and a data access layer. This means you’ll be confronted with a huge amount of code samples. You’ve been warned!  

Spotting the benefits of using a service layer: recreating a typical scenario

As I explained in the introduction, the implementation of a service layer can be really useful in the development of enterprise-level web applications, with a rich domain model entirely decoupled from the storage layer. It’s possible, though, that this concept sounds somewhat confusing to you.

Therefore, before I start out constructing the aforementioned user service, I’d like you to take a quick look at the following code fragments. Hopefully, they will make things a bit clearer. The first one implements a basic, naive action user controller. It utilizes a simple model to perform CRUD operations in a relational database.  

This sample class looks like this:

(UserController.php)

<?php

class UserController
{
    protected $_userModel;
    protected $_view;
   
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->_userModel = ModelHelper::getModel(‘users’);
    }
   
    /**
     * Fetch all users from the storage
     */  
    public function indexAction()
    {
        $users = $this->_userModel->fetchAll();
        $this->_view->users = $users;
        $this->_view->render();
    }
   
     /**
      * Save a new user to the storage ($data comes possibly from a POST request)
      */
     public function insertAction(array $data)
     {
         $this->_userModel->insert($data);
         // redirect to ‘indexAction()’ here…
     }
    
     /**
      * Update the specified user in the storage ($data comes possibly from a POST request)
      */
     public function updateAction($id, $data)
     {
         $this->_userModel->update($id, $data);
         // redirect to ‘indexAction()’ here…
     }
    
     /**
      * Delete the specified user from the storage
      */
     public function deleteAction($id)
     {
         $this->_userModel->delete($id);
         // redirect to ‘indexAction()’ here…
     }   
}

Even though I deliberately omitted the code for the error handling parts, the implementation of the above action controller is fairly easy to follow. It simply grabs an instance of a user model in the constructor (via a static service locator), which is then utilized for fetching, saving and deleting user data from the storage layer.

As I noted earlier, models that are coupled to the storage are rigid, inflexible elements that can’t be ported to a different infrastructure. Of course, it’s possible to use DAOs or data mappers to break up this coupling.

But what if it’s necessary to do something else, like querying an external API, or processing a batch of entities in some additional form? Making the user controller directly consume the mappers, along with the external API or whatever is required, would create a mess of bloated code.    

This is exactly where the service layer comes into play. If you’re curious to see how the pattern solves such a mess, the following snippet shows the refactored version of the previous user controller. Check it out:

(UserController.php)

<?php

class UserController
{
    protected $_userService;
    protected $_view;
   
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->_userService = ServiceHelper::getService(‘users’);
    }
   
    /**
     * Fetch all users from the storage
     */  
    public function indexAction()
    {
        $users = $this->_userService->fetchAll();
        $this->_view->users = $users;
        $this->_view->render();
    }
   
     /**
      * Save a new user to the storage ($data comes possibly from a POST request)
      */
     public function insertAction(array $data)
     {
         $this->_userService->insert($data);
         // redirect to ‘indexAction()’ here…
     }
    
     /**
      * Update the specified user in the storage ($data comes possibly from a POST request)
      */
     public function updateAction($id, $data)
     {
         $this->_userService->update($id, $data);
         // redirect to ‘indexAction()’ here…
     }
    
     /**
      * Delete the specified user from the storage
      */
     public function deleteAction($id)
     {
         $this->_userService->delete($id);
         // redirect to ‘indexAction()’ here…
     }   
}

Though subtle, the way that the controller has been implemented is radically different. Now, the class consumes a user service object, which is responsible for running CRUD operations. There’s no direct interaction with a specific model, or even a clue as to how these operations are internally executed. What’s more, the same service could be used within a different controller or client layer, and no code duplication would occur. With that said, are you beginning to see the real benefits to using a service layer? I guess you do.

And now that you have a clearer idea of how to utilize the pattern within an MVC schema, it’s time to start implementing it as a standalone structure. Since my goal here is to build a user service from scratch, in the next segment I’ll be developing a basic domain layer which will be made up of a few user entities.    

To see how this brand new layer will be constructed, click on the link below and keep reading.

{mospagebreak title=Taking the first step: modeling domain objects}

As I explained in the preceding section, the first step toward the implementation of a user service layer is to set up a basic domain model. This model will contain some user objects. To accomplish this, in this case I’m going to use the following abstract class, responsible for modeling generic entities:

(MyApplication/Entity/AbstractEntity.php)

<?php

namespace MyApplicationEntity;

abstract class AbstractEntity
{
    protected $_values = array();
    protected $_allowedFields = array();
   
    /**
     * Class constructor
     */
    public function __construct(array $data)
    {
        foreach ($data as $name => $value) {
            $this->$name = $value;
        }
    }
   
    /**
     * Assign a value to the specified field via the corresponding mutator (if it exists);
     * otherwise, assign the value directly to the ‘$_values’ protected array
     */
    public function __set($name, $value)
    {  
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException(‘The field ‘ . $name . ‘ is not allowed for this entity.’); 
        }
        $mutator = ‘set’ . ucfirst($name);
        if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
            $this->$mutator($value);          
        }
        else {
            $this->_values[$name] = $value;
        }   
    }
   
    /**
     * Get the value assigned to the specified field via the corresponding getter (if it exists);
    otherwise, get the value directly from the ‘$_values’ protected array
     */
    public function __get($name)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException(‘The field ‘ . $name . ‘ is not allowed for this entity.’);   
        }
        $accessor = ‘get’ . ucfirst($name);
        if (method_exists($this, $accessor) && is_callable(array($this, $accessor))) {
            return $this->$accessor;   
        }
        if (isset($this->_values[$name])) {
            return $this->_values[$name];  
        }
        throw new EntityException(‘The field ‘ . $name . ‘ has not been set for this entity yet.’);
    }

    /**
     * Check if the specified field has been assigned to the entity
     */
    public function __isset($name)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException(‘The field ‘ . $name . ‘ is not allowed for this entity.’);
        }
        return isset($this->_values[$name]);
    }

    /**
     * Unset the specified field from the entity
     */
    public function __unset($name)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException(‘The field ‘ . $name . ‘ is not allowed for this entity.’);
        }
        if (isset($this->_values[$name])) {
            unset($this->_values[$name]);
        }
    }

    /**
     * Get an associative array with the values assigned to the fields of the entity
     */
    public function toArray()
    {
        return $this->_values;
    }             
}

 

(MyApplication/Entity/EntityException.php)

<?php

namespace MyApplicationEntity;

class EntityException extends Exception{}

As shown above, the “AbstractEntity” class is pretty similar to others that you’ve probably seen in articles previously published here at the Developer Shed network. In short, this class simply implements some magic PHP methods, like “__get()” and “__set(),” which are used to dynamically assign and retrieve the values assigned to the fields of a generic entity via the corresponding mutators/getters (if they’ve been implemented). Its driving logic is that simple.

So far, so good. With this abstract class neatly modeling the structure and minimal behavior of generic domain objects, you may be wondering what comes next, right? Since the service layer that I plan to build here will manipulate user entities, it’s necessary to spawn, from the previous abstract parent, a subclass that specifically models such objects.

Fear not, as the implementation of this derivative will be shown below. Just keep reading.   

Going one step further: modeling user entities

In reality, creating a subclass that exclusively models user entities is a simple process. If you’re still reluctant to believe in my words, take a peek at the following code fragment, which shows the implementation of this derivative:

(MyApplication/Entity/User.php)

<?php

namespace MyApplicationEntity;

class User extends AbstractEntity
{  
    protected $_allowedFields = array(‘id’, ‘fname’, ‘lname’, ‘email’);
   
    /**
     * Set the user’s ID
     */
    public function setId($id)
    {
        if(!filter_var($id, FILTER_VALIDATE_INT, array(‘options’ => array(‘min_range’ => 1, ‘max_range’ => 999999)))) {
            throw new EntityException(‘The specified ID is invalid.’);
        }
        $this->_values['id'] = $id;
    }
   
    /**
     * Set the user’s first name
     */ 
    public function setFname($fname)
    {
        if (strlen($fname) < 2 || strlen($fname) > 32) {
            throw new EntityException(‘The specified first name is invalid.’);
        }
        $this->_values['fname'] = $fname;
    }
       
    /**
     * Set the user’s last name
     */
    public function setLname($lname)
    {
        if (strlen($lname) < 2 || strlen($lname) > 32) {
            throw new EntityException(‘The specified last name is invalid.’);
        }
        $this->_values['lname'] = $lname;
    }
   
    /**
     * Set the user’s email address
     */
    public function setEmail($email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new EntityException(‘The specified email address is invalid.’);
        }
        $this->_values['email'] = $email;
    }                   
}

Even though at first glance it looks rather complex, don’t be fooled. The earlier “User” class is nothing but a simple data holder (with a few specific constraints) which implements some mutators to store the ID, the first and last names of a user, and their email address.

With this subclass already up and running, I finally managed to set up a basic, clean domain model, which is composed of plain user objects. Of course, there’s no much that can be done with this layer on its own; the implementation of a fully-functional user service requires building mapping and data access layers as well. However, this is a work in progress; these additional structures will be developed in upcoming installments of this series.  

Final Thoughts

In this first part of the series, I attempted to provide you with an introduction to what a service layer is and how to use it in conjunction with an MVC stack. As with many other enterprise-level patterns, you don’t need to use a service layer in every possible use case. However, its implementation can be of great help for interfacing sections of an application with multiple client layers, such as the action controllers that you’ll see in many current MVC frameworks.

In addition, I’ve started developing the domain layer of a sample program. It will employ a service for manipulating user data, independently of the underlying infrastructure. At this point, this domain model is pretty anemic. It’s currently composed of only a few user entities, which live in happy isolation.

With this structure already set, the next step is to create some data mappers, which will act like mediators between the entities nd the persistence layer. The creation of the mappers will be discussed in depth in the upcoming tutorial.

Don’t miss the next part!

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan