Design Patterns – What are Repositories, Services, and Actions/Controllers?

design-patternsobject-oriented-design

I started a project using Slim3 and PHP using limited knowledge of application architecture. The plan was to create the project and separate application concerns. It was all going well, but things got confusing fast as the application grew.

The whole idea for doing this was to make development easier. It does in a way, but I find it's complex at times to keep tabs on the data flow.

I need some advice on what Repositories, Services and Controllers/Actions are. And how they should work in a system. My current understanding of them is below:

Repository

Repositories are used between the service layer and the model layer. For example, in a UserRepository you would create methods that contains the code to read/write from the database. In PHP, PDO would be used, or an ORM, within the repo methods. For example:

class UserRepository
{
    public function findByID($id) { ... }
    public function findByEmail($email) { ... }
    public function findByMobile($mobile) { ... }
    public function createEmail($email, $firstname, $lastname, $password) { ... }
    public function createMobile($mobile, $firstname, $lastname, $password) { ... }
}

I've put a few example methods in there. But there would likely be many more.

Services

The service layer encapsulates application logic. For example, the UserService would be responsible for creating an account, and performing the required logic in order to register the user. Services can also be third party, for example creating a service for Facebook's SDK, or ORM.

An example service:

class UserService
{
    public function createMobile($mobile, $firstname, $lastname, $password)     {
    /*
     * Call a validation service to validate input
     */
    ...

    /*
     * Use UserRepository's findByMobile() to check if account exists
     */
    ...

    /*
     * Use UserRepository's createMobile() to create account
     */
    ...

    /*
     * Call SMS service to send verification code
     */
    ...
    }

    public function createEmail(...) { ... }
    public function getFollowers (...) { ... }
}

Actions

I'm not sure if this is a real term. It's used in the Slim Framework documentation and seems to represent a thin controller.

An Action contains very little logic and is used to make calls to services. Rarely does the Action make direct calls to the repositories unless there's a valid reason. The Action will perform basic checks on the data returned from the services in order to send a response back to the client.

They're tied to individual routes. I'm using them like so:

class ActivateEmailAction extends Action {

    public function __invoke(Request $request, Response $response, $args = [])
    {
        if(!$this->ci->ActivationService->activateEmail($args['token'])){
            return $response->withJson([
                'status' => 'error',
                'data' => null,
                'message' => 'Invalid verification token'
            ]);
        };

        return $response->withJson([
            'status' => 'success',
            'data' => null,
            'message' => null
        ]);
    }
}

Am I using these patterns correctly? The flow I seem to have adopted is like so:

  1. Everything starts at the route. For example a request is made to /create. The route is registered to an Action.
  2. Action decides what services to call
  3. Services perform the logic, make calls to other services and repositories if required
  4. Service hands back data to the Action
  5. Action returns a response

Any advice would be much appreciated.

Best Answer

I actually like the way you are implementing this.

Some answers:

What are repositories, services and actions/controllers?

  • Repositories: The repository is a gateway between your domain/business layer and a data mapping layer, which is the layer that accesses the database and does the operations. Basically the repository is an abstraction to you database access.

  • Service: The service should provide an API to your business logic, therefore being an abstraction to your repository, here is where I disagree, just a little, with @Cerad, the services should be the only ones with access to the repositories, otherwise it violates the Dependency Inversion Principle (D in SOLID), because the business layer is an abstraction of your data access layer.

  • Actions/Controllers: The A/C objects works as a gateway between your input and the domain logic, is decides what to do with the input and how to output the response.

I find it's complex at times to keep tabs on the data flow.

Yes, sometimes it is, but as I read, and don't remember well, that it is better to have organized and principle based code than to have latter difficulties on ugly code, it makes it more manageable, readable and maintainable.

Finally:

The architectural model you are using is the Layered Architecture, everything is separated in layers, and the upper layers are abstractions of the lower ones, that's why every layer should only reference the immediate lower layer.

Hope this helps.