Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Lesson 4 of the Advanced Laravel: Architecture, Scaling & Performance course
LaravelJune 27, 20263 min read

Utilizing Data Transfer Objects (DTOs) for Type-Safe Laravel

Stop passing associative arrays through your application. Learn to use DTOs to enforce type safety and data integrity across your Laravel architecture.

LaravelDTOArchitectureDDDType SafetyPHPbackend

Previously in this course, we explored implementing action classes to encapsulate our business logic. While actions keep our controllers thin, passing loose associative arrays between them creates a "hidden contract" problem where types are guessed and keys are easily forgotten.

This lesson adds Data Transfer Objects (DTOs) to our architectural toolkit. By replacing unstructured arrays with strict, immutable objects, we gain compile-time confidence and runtime Data Integrity.

The Problem with Associative Arrays

In a high-traffic SaaS environment, an associative array is a liability. Consider a CreateSubscription action:

PHP
#6A9955">// The "Array Hell" approach
public function handle(array $data) {
    return Subscription::create([
        'user_id' => $data['user_id'], #6A9955">// What if this is missing?
        'plan_id' => $data['plan_id'], #6A9955">// Is this an int or a string?
    ]);
}

When you pass an array, you lose static analysis capabilities. You don't know if user_id is present, if it's the right type, or if the key was typoed as userid. By utilizing a DTO, we formalize the schema.

Creating Type-Safe DTOs

A DTO is a simple object whose only job is to carry data. In modern PHP, we use readonly properties and constructor promotion to keep them concise and immutable.

PHP
namespace App\DTOs;

readonly class CreateSubscriptionDTO
{
    public function __construct(
        public int $userId,
        public string $planId,
        public ?string $promoCode = null,
    ) {}

    public static function fromRequest(\Illuminate\Http\Request $request): self
    {
        return new self(
            userId: (int) $request->user()->id,
            planId: $request->validated('plan_id'),
            promoCode: $request->validated('promo_code'),
        );
    }
}

By defining this class, we ensure that any service receiving this DTO knows exactly what data is available. This is the foundation of Type Safety in our domain layer.

Validating Data Flow Across Services

To ensure our DTOs are always valid, we perform validation before instantiation. We use Laravel's FormRequest to handle the heavy lifting of input validation, then hydrate the DTO.

In our project, we are currently re-architecting our billing module. Instead of passing the request object into our ProcessPayment action, we now require a PaymentDTO.

PHP
#6A9955">// Inside a Controller
public function store(PaymentRequest $request)
{
    $dto = PaymentDTO::fromRequest($request);
    
    #6A9955">// The action now has a strict contract
    return $this->paymentAction->execute($dto);
}

This pattern ensures that by the time our domain logic runs, the data is already cleaned, cast, and validated.

Hands-on Exercise

  1. Define a DTO: Create a UserRegistrationDTO in app/DTOs that accepts email (string), name (string), and age (int).
  2. Implement Hydration: Add a static fromArray(array $data) method to your DTO that performs manual casting.
  3. Refactor an Action: Take an existing action that accepts an array and update the signature to accept your new UserRegistrationDTO.

Common Pitfalls

  • Over-engineering: Do not turn your DTOs into mini-models. They should not contain business logic or database interactions. If you find yourself adding methods to query the database, you've crossed the line into Entity territory.
  • Performance overhead: While creating objects has a negligible memory cost, avoid creating thousands of DTOs inside a tight loop if you are processing massive datasets.
  • Incomplete Hydration: Always ensure your DTOs are fully populated. If a property can be null, explicitly mark it as nullable (?string) rather than omitting it, to keep the contract clear.

Recap

We've moved from passing "blind" arrays to using strict, immutable DTOs. This change shifts our error surface from runtime (where things break in production) to development time (where IDEs and static analysis tools like PHPStan can catch our mistakes). We have enforced a strict data contract that will make our SaaS platform significantly easier to refactor as we scale.

Up next: Service Layer Pattern — we'll learn how to orchestrate these DTOs across multiple domains using dedicated service classes.

Previous lessonImplementing Action ClassesNext lesson Service Layer Pattern
Back to Blog

Similar Posts

LaravelJune 28, 20263 min read

Testing DDD Components: Isolating Domain Logic in Laravel

Master Testing DDD components in Laravel. Learn to mock external services, isolate domain logic, and write reliable PHPUnit tests for your Action classes.

Read more
LaravelJune 27, 20263 min read

Modular Monolith Structure: Domain-Driven Scaling in Laravel

Learn to build a Modular Monolith by structuring your Laravel directory by domain, enforcing encapsulation, and defining public interfaces for module communication.

Part of the course

Advanced Laravel: Architecture, Scaling & Performance

advanced · Lesson 4 of 57

  1. 1

    Transitioning from MVC to DDD

    3 min
  2. 2

    Defining Bounded Contexts

    3 min
  3. 3

    Implementing Action Classes

    3 min
Read more
LaravelJune 27, 20264 min read

Service Layer Pattern: Achieving True Decoupling in Laravel

Master the Service Layer pattern in Laravel to decouple domain logic from your controllers. Improve testability, maintainability, and clean architecture.

Read more
4

Utilizing Data Transfer Objects (DTOs)

3 min
  • 5

    Service Layer Pattern

    4 min
  • 6

    Modular Monolith Structure

    3 min
  • 7

    Querying with Strict Eloquent

    4 min
  • 8

    Advanced Subqueries and Joins

    4 min
  • 9

    Raw Expressions for Performance

    4 min
  • 10

    Advanced Indexing Strategies

    4 min
  • 11

    Database Partitioning Techniques

    4 min
  • 12

    Read/Write Database Splitting

    4 min
  • 13

    Handling Multi-Database Connections

    3 min
  • 14

    Eloquent Caching Strategies

    3 min
  • 15

    Queue Worker Prioritization

    4 min
  • 16

    Unique Job Patterns

    4 min
  • 17

    Rate Limiting Background Jobs

    3 min
  • 18

    Event-Driven Architecture

    4 min
  • 19

    Integrating External Message Brokers

    4 min
  • 20

    Distributed Transactions and Sagas

    3 min
  • 21

    Eventual Consistency Patterns

    4 min
  • 22

    Multi-Layered Caching Strategy

    4 min
  • 23

    Cache Tagging and Invalidation

    4 min
  • 24

    Session Persistence in Clusters

    4 min
  • 25

    High-Availability Infrastructure

    4 min
  • 26

    Zero-Downtime Deployment Pipelines

    4 min
  • 27

    Advanced OAuth2 Implementation

    3 min
  • 28

    JWT and Stateless Security

    4 min
  • 29

    Multi-Tenant Security Isolation

    3 min
  • 30

    Defense Against SSRF

    3 min
  • 31

    Mass Assignment Hardening

    4 min
  • 32

    Automated Security Testing

    3 min
  • 33

    Custom Telemetry Design

    3 min
  • 34

    Distributed Tracing

    4 min
  • 35

    Profiling PHP Execution

    3 min
  • 36

    Memory Management in Long-Running Processes

    4 min
  • 37

    Testing DDD Components

    3 min
  • 38

    Contract Testing

    3 min
  • 39

    Handling Large File Uploads

    3 min
  • 40

    Optimizing Asset Pipelines

    4 min
  • 41

    Database Query Caching Layers

    3 min
  • 42

    Advanced Eloquent Scopes

    4 min
  • 43

    Distributed Locks

    3 min
  • 44

    API Versioning Strategies

    4 min
  • 45

    Database Migration Strategies

    4 min
  • 46

    Handling Webhooks Securely

    3 min
  • 47

    Advanced Logging Patterns

    3 min
  • 48

    Database Indexing for Joins

    4 min
  • 49

    Graceful Degradation

    3 min
  • 50

    Custom Middleware Development

    Coming soon
  • 51

    Database Connection Pooling

    Coming soon
  • 52

    Handling Large Data Exports

    Coming soon
  • 53

    Security Header Configuration

    Coming soon
  • 54

    Database Sharding Concepts

    Coming soon
  • 55

    Real-time Data Synchronization

    Coming soon
  • 56

    Database Deadlock Prevention

    Coming soon
  • 57

    Managing Third-Party API Integrations

    Coming soon
  • View full course