Service Layer Pattern

 
Example that simply simulate the action of sending an email by printing text.
This shows the Service Layer Pattern clearly.

You move all email logic to a dedicated class.
Controllers stay clean, emails become testable.

-------------------------------------------------

Project Folder Structure (PSR-4 Compatible)

project/
│
├── composer.json
├── vendor/                (created after composer install)
│
├── src/
│   ├── Email/
│   │   ├── GenericEmail.php
│   │   └── EmailService.php
│   │
│   └── App.php            (example controller-like file)
│
└── index.php  

-------------------------------------------------

composer install
composer dump-autoload

-------------------------------------------------

Why Not a Single EmailService?

If you put everything inside EmailService (view rendering, subject, attachments), 
you end up with a class that:
- Has too many responsibilities
- Is harder to test
- Becomes messy once you add more email types
- Is tightly coupled to the internal email-building logic

Example:
What happens when you need a weekly report email, a password reset email, etc.?

If all email-building logic lives inside EmailService, 
that class becomes a "God Class" quickly.

Generic Class

 
namespace App\Email; 

class GenericEmail
{
    public function __construct(
        public string $to, 
        public string $subject, 
        public string $body, 
        public array $attachments) {}

    public function build(): object
    {
        // Simulate mailable object
        $mail = [
            "to" => $this->to,
            "subject" => $this->subject, 
            "body" => $this->body,
            "attachments" => $this->attachments ?? [],
        ];

        // Add attachments
        foreach($this->attachments as $path) {
            $this->attach($path);
        }

        // Return mailable
        return (object) $mail;
    }

    public function attach(string $filePath): self
    {
        $this->attachments[] = $filePath;
        return $this;
    }
}

Service

 
namespace App\Email;

class EmailService
{
    public function send(GenericEmail $genericEmail): void
    {
        // Build email
        $email = $genericEmail->build();
        
        // Simulate sending
        echo "\n EMAIL SENT / Subject: {$email->subject} ";
        foreach ($email->attachments as $path) {
            echo " / $path";
        }

        /*
            EMAIL SENT / Subject: Welcome  / welcome_guide.pdf
            EMAIL SENT / Subject: Reset Password  / instructions.txt
        */
    }
}

Application

 
namespace App;

use App\Email\EmailService;
use App\Email\GenericEmail;

class App
{
    public function run(): void 
    {
        $service = new EmailService();

        // Welcome email
        $service->send(new GenericEmail(
            "john@example.com",
            "Welcome",
            "Hello John, welcome!",
            ["welcome_guide.pdf"]
        ));

        // Password reset email
        $service->send(new GenericEmail(
            "john@example.com",
            "Reset Password",
            "Click this to reset password.",
            ["instructions.txt"]
        ));
    }
}

Usage Example

 
require __DIR__ . '/vendor/autoload.php';

$app = new App\App();
$app->run();






Questions and answers:
Clink on Option to Answer




1. What is the main purpose of the Service Layer Pattern?

  • a) To move business logic out of controllers into dedicated classes
  • b) To store HTML templates

2. Why is it bad to put all email logic inside a single EmailService?

  • a) It makes the class easier to extend
  • b) It creates a “God Class” with too many responsibilities

3. What does the GenericEmail class represent?

  • a) A blueprint for building different kinds of emails
  • b) A controller for handling web requests

4. What does the build() method in GenericEmail do?

  • a) Generates the mailable object (simulated)
  • b) Sends the email immediately

5. What is the responsibility of EmailService?

  • a) Build and send the email logic itself
  • b) Only handle the sending part while email-building stays elsewhere

6. Why does the controller (App) stay clean?

  • a) Because it delegates work to the service layer
  • b) Because it contains all email logic

7. Why is using a dedicated email class beneficial?

  • a) It makes emails reusable, testable, and easy to extend
  • b) It prevents adding new email types