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"]
));
}
}
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