Service Layers: Should it hide the domain models' API?

Go To StackoverFlow.com

1

I'm loading up on Zend 1.11 and integration with Doctrine 1.2, and have been reading a lot about the use of Service Layers.

To my understanding, Service Layers sits on top of the business logic, adding a layer between controllers and the domain models.

But, I'm stuck on how to actually implement a service layer, mainly because I am a bit unsure to what type of APIs to the service class I should be defining.

First, given a service class, App_Service_Memberships which will handle the business entities group with App_Model_Group and users as App_Model_User:

Should the APIs of the service class expose the composition of the classes by accepting or returning instances of the said domain models?

Or should the APIs of the service class hide the composition of the classes by only accepting or returning native data types like integers or arrays?

Like say:

class App_Service_Memberships
{

    public function addUserToGroup($user_id, $group_id)
    {

        //Create or retrieve domain models here and operate on them; handle persistence.
        $membership = new App_Model_Membership();

        $membership->member->user_id = $user_id;
        $membership->group->group_id = $group_id;
        $membership->join_date = date("Y-m-d H:i:s");

        $membership->save();

        return $membership->toArray();
    }

    public function getMembersOfGroup($group_id)
    {
         $groupMembers = array();

         //Query objects here with Doctrine or the ORM of choice...

         foreach($results as $membership){
             $groupMembers[] = $membership->member->toArray();
         }

         return $groupMembers;
    }
}

Where the controller would just pass in integers as parameters (Data from forms, or output from other service classes) to the service class, totally oblivious of the domain models involved.

as opposed to:

class App_Service_Memberships
{
    public function addUserToGroup(App_Model_UserInterface $user, App_Model_Group $group)
    {
        //Create or retrieve domain models here and operate on them; handle persistence.
        $membership = new App_Model_Membership();

        $membership->member = $user;
        $membership->group = $group;
        $membership->join_date = date("Y-m-d H:i:s");

        $membership->save();

        return $membership;
    }

    public function getMembersOfGroup(App_Model_Group $group)
    {
         $groupMembers = array();

         //Query memberships here

         foreach($results as $membership){
             $groupMembers[] = $membership->member;
         }

         return $groupMembers;
    }
}

Where the controller does get to handle the domain models within its scope, and pass them to the service class?

Which do you consider as the better practice?

2012-04-05 16:03
by Bez Hermoso


1

...should the APIs of the service class hide the composition of the classes by only accepting or returning native data types like integers or arrays?

Yes, this is ideal. If you force clients (controllers in this case) of your service to pass in a pre-constructed domain entity (user), you force the client to know a lot about the domain. It shouldn't.

Your service methods can also be static (but they don't have to be).

You don't really need the App_ prefix on your service classes. Not sure when this got started in the ZF community, but it has been cargo-culted to death and really isn't necessary. I generally put services (and everything application specific) under lib. To be more specific, I use src/main/lib which works well with src/test/* and src/vendor/lib.

As for return values...I generally have service methods return some sort of collection. An array is fine; however, a richer collection object is very helpful in many cases (but not compulsory). Sometimes I just use a Zend Paginator object to be pragmatic about it. It isn't the richest collection object available, but it works OOTB.

2012-04-05 20:56
by Wil Moore III
Ads