DEV Community

Cover image for "Domain-Driven Laravel" build great systems that are scalable and powerful
oskhar
oskhar

Posted on

"Domain-Driven Laravel" build great systems that are scalable and powerful

In the ever-expanding universe of software development, creating scalable, maintainable, and powerful systems is no small feat. With so many frameworks, tools, and patterns vying for your attention, it's easy to feel like a lost space traveler, orbiting without direction. But fear not, fellow developer! 🌌 The Domain-Driven Laravel repository is here to guide you through the cosmos of RESTful API development, using the Domain-Driven Design (DDD) methodology.

https://github.com/oskhar/domain-driven-laravel

GitHub logo oskhar / domain-driven-laravel

🛸 🔃 A pre-configured Laravel 11.x template crafted to facilitate the development of web applications following Domain-Driven Design (DDD) principles.

Domain-Driven Laravel 🛸 🔃

MIT License

A robust, scalable, and flexible architecture for developing RESTful APIs with Laravel using Domain-Driven Design (DDD) principles.

📘 Article

Introduction

Laravel is an excellent framework for building Powerfull Apps, offering a rich set of features and a clean syntax. However, as projects grow in complexity, it's easy for the codebase to become unmanageable. The absence of a clear architectural pattern can lead to a mix of responsibilities, making the code harder to maintain and scale.

This repository presents a way to structure Laravel projects using Domain-Driven Design (DDD) principles, allowing for better organization, scalability, and separation of concerns. The approach showcased here is inspired by best practices and aims to solve real-world challenges in a practical and maintainable way.

The goal is to provide a solid foundation for building Laravel applications that are.

  • 🧩 Easy to Understand. A well-organized codebase with clear boundaries.
  • 🛠️ Maintainable. Follow…

In this article, we’ll explore the galaxy of this remarkable Laravel package, uncover its unique features, and see why it's perfect for developers looking to build sophisticated systems. Buckle up, space cowboy, because we’re about to launch! 🚀

directory structure: https://github.com/oskhar/domain-driven-laravel/blob/main/docs/project-structure.md

What Is Domain-Driven Laravel?

"Domain-Driven Laravel 🛸 🔃" is a structured approach to building RESTful APIs using Laravel, centered around the principles of Domain-Driven Design (DDD). This package allows you to structure your application logically by grouping related business logic into domains, making your system easier to scale and maintain.

By leveraging Laravel’s robust architecture with the organizational power of DDD, this repository helps developers create well-organized APIs that are as efficient as they are powerful.

Why Domain-Driven Design?

Domain-Driven Design provides a clear structure for separating concerns and organizing your application into manageable, understandable parts. It focuses on defining the core domain and domain logic (the heart of your business logic) and keeps your application modular.

Imagine having your system organized like planets orbiting a star, each with a well-defined purpose and connection to the larger system. With DDD, you’ll have domains such as User Management, Product Management, and more, each managing its own gravitational pull in the API ecosystem.

The real magic of "Domain-Driven Laravel 🛸 🔃" is in its thoughtful implementation of these concepts, transforming Laravel into a well-oiled machine of interconnected domains. You can now build applications that are scalable and ready for the complexities of the real world.

The Power of Error Handling: Laughing Through the Cosmos 🤖

If you’re like most developers, you’ve encountered your fair share of error messages. But have you ever had an error handler that insults you for making a mistake? Welcome to the world of "Domain-Driven Laravel 🛸 🔃", where error handling is not just functional—it’s personal and hilarious!

This repo offers a built-in error-handling mechanism that not only returns the expected HTTP status codes but also scolds you for making mistakes. Let’s break down some of these responses:

$exceptions->render(
    fn(QueryException $exception, $request) => ($response)(
        APIResponseData::from([
            "status" => false,
            "errors" => [
                "Bro wrote the wrong database query. Backend skills issue.",
                $exception->getMessage()
            ]
        ]),
        APIStatusEnum::INTERNAL_SERVER_ERROR
    )
);
Enter fullscreen mode Exit fullscreen mode

The moment you make a bad database query, you’ll be greeted with a response like:

"Bro wrote the wrong database query. Backend skills issue."
Enter fullscreen mode Exit fullscreen mode

Instead of a typical, dry error message, the system nudges you to improve your backend skills—sometimes with a bit of attitude!

Other responses include:

  • Array Structure Gone Wrong:

    "Ayyo, looks like your backend messed up the array structure."
    
  • Bad Method Call:

    "Are you sure backend bro? The method you called doesn't exist."
    
  • Undefined Exception:

    "Your backend is dumb, bro."
    

This unique approach not only provides you with helpful information but adds a fun twist to your debugging experience. It turns those dreaded errors into moments of levity, reminding you that even in the vastness of code, a little humor can go a long way.

Response Structure.

Thanks to the well-defined structure of the API response, all errors, including these personalized ones, will follow a consistent format. The APIResponseData class ensures that the response is structured like this:

class APIResponseData extends Data
{
    public function __construct(
        readonly ?bool $status = true,
        readonly ?string $message,
        readonly mixed $data = null,
        /** @var array<string> */
        readonly ?array $errors,
        readonly ?PaginationData $pagination,
        readonly ?APIMetaData $meta,
    ) {
    }
}
Enter fullscreen mode Exit fullscreen mode

Here’s how a 500 Internal Server Error might look:

// Example 500 Internal Server Error
{
    "status": false,
    "message": "Galactic disruption. An unexpected cosmic event occurred!",
    "errors": [
        "Bro wrote the wrong database query. Backend skills issue",
        "{{ Query error messages specifically }}"
    ],
    "meta": {
        "request_id": "string",
        "response_size": "integer|byte"
    }
}
Enter fullscreen mode Exit fullscreen mode

This structure provides clarity and consistency, ensuring that every response, whether success or failure, is predictable and easy to handle on the client side.

Default Messages: Cosmic Wisdom 🌠

Another shining feature of this repository is the default message handling for API responses. If you forget to set a message on your response, you won't just get a generic fallback—you’ll get a galactic-themed message that makes your API feel like a trip through the stars.

Here’s a sample of the default messages:

  • 200 SUCCESS: "Success! Your request has safely landed back to Earth."
  • 201 CREATED: "New entity launched into the cosmos."
  • 400 BAD_REQUEST: "Your request veered off course and couldn't escape Earth's gravity!"
  • 401 UNAUTHORIZED: "Your credentials don't pass the cosmic gatekeeper!"
  • 404 NOT_FOUND: "The data you're seeking is beyond the bounds of space!"
  • 500 INTERNAL_SERVER_ERROR: "Galactic disruption. An unexpected cosmic event occurred!"

These thematic responses don’t just provide a fun flavor to your API—they also make it clearer to clients and users what’s happening under the hood.

For example, if your request hits a 404, instead of a boring "Not Found" message, you’ll receive a cosmic-themed error:

"The data you're seeking is beyond the bounds of space!"
Enter fullscreen mode Exit fullscreen mode

This approach not only enriches the developer experience but also makes the API more user-friendly. Your clients and users will enjoy these little touches of humor and personality.

Going Beyond: What Else Makes This Repository Stellar? 💫

"Domain-Driven Laravel 🛸 🔃" isn't just about humor and cosmic messages. It's a fully fleshed-out package that makes it easier to manage your Laravel applications using DDD principles. Let’s take a look at some of the other key features:

1. Modular Domain Design.

With a clean and modular architecture, you can easily organize your application into domains, each with its own logic and responsibility. This separation allows for better scaling, testing, and maintenance.

2. Built-in API Response Management.

Handling API responses is a breeze with a consistent structure that ensures all responses are formatted correctly. Whether you’re returning success, error, or validation messages, the built-in API response handler will make sure everything is in its right place.

3. Error Handling that Learns.

While the humorous error handling adds personality, it also comes with a solid system that tracks and logs exceptions in a way that helps you debug and improve your code.

4. Advanced Middleware.

The repository includes advanced middleware implementations that ensure all parts of your application are in sync with the domain rules and API structure. With these middleware hooks, you can ensure that your application always behaves as expected.

5. Integration with Spatie's Packages.

Leverage the power of Spatie’s robust Laravel packages for roles, permissions, and data handling. This repo comes with pre-configured support for Spatie’s tools, giving you the best of both worlds: the organization of DDD and the strength of Laravel’s best packages.

Simple Usage: Focus on Domain Actions 🛠️

When working with the repository, simplicity is key. The goal is for developers to focus purely on domain actions without worrying about infrastructure concerns. This clear separation of responsibilities ensures that each domain handles its own business logic while leaving shared services and external integrations to other layers.

1. Stay Focused on Domain Actions.

In this structure, all core logic related to a specific domain is encapsulated in Actions. You don’t need to think about cross-domain interactions or infrastructure concerns—just focus on building the actions that power your domain. For example, an action like CreateUserAction lives entirely within the User domain and manages user creation. You can call this action from a controller or another action, keeping your code concise and easy to manage.

namespace Domain\User\Actions;

use Domain\User\Models\User;
use Domain\User\Data\UserData;

class CreateUserAction
{
    public function execute(UserData $data): User
    {
        return User::create($data);
    }
}
Enter fullscreen mode Exit fullscreen mode

This straightforward action does its job without needing to handle infrastructure-level details like logging, caching, or external API calls. Those concerns are dealt with in the Infrastructure layer or the Shared domain, keeping your actions clean and single-focused.

2. Shared Domain for Cross-Cutting Concerns.

Any service that spans across multiple domains, such as authentication, logging, or notifications, can be placed in the Shared domain. This prevents domain entanglement and ensures that the logic stays modular and focused.

For example, a notification service can live in the Shared domain, allowing any domain to trigger notifications without duplicating code.

namespace Domain\Shared\Services;

class NotificationService
{
    public function sendNotification(UserData $user, string $message): bool
    {
        // Logic for sending notifications
    }
}
Enter fullscreen mode Exit fullscreen mode

Any domain that needs to notify users can simply call this service, ensuring that the NotificationService is consistent across the application.

3. Infrastructure for External Integrations.

The Infrastructure layer handles external services and integrations. This includes third-party APIs, payment gateways, or database configurations. By keeping external integrations here, your domain actions remain focused on business logic without worrying about how the external world works.

For instance, a payment gateway service could be handled in Infrastructure, keeping payment logic separate from core domain actions.

namespace Infrastructure\Services;

class PaymentGatewayService
{
    public function processPayment(PaymentDetailsData $details): mixed
    {
        // Payment processing logic
    }
}
Enter fullscreen mode Exit fullscreen mode

With this structure, domain actions can call on external services when needed, but the bulk of the integration code is abstracted away, keeping your business logic clean and independent.

4. Flexibility with Interfaces.

To enhance the repository's flexibility and error prevention, developers who are comfortable using interfaces can incorporate a dedicated Interfaces folder. This addition provides a structured way to manage potential changes, such as migrations or dependency removals, without impacting the core functionality. The minimalist design of this repository ensures that it remains adaptable to various development needs, and the use of interfaces aligns with this principle by offering a safeguard against unforeseen changes.

app
├── Console                     # Custom Artisan commands
├── Domain                      # Core domain logic and business rules
├── Infrastructure              # Infrastructure-related code
└── Interfaces                  # Additional Folder
Enter fullscreen mode Exit fullscreen mode

This approach allows developers to define contracts for their actions, services, or any other components that may evolve over time, ensuring that the code remains stable and maintainable across different stages of development.

5. No Domain Interference.

One of the core principles of "Domain-Driven Laravel 🛸 🔃" is that each domain should remain isolated from others. Domains should not interfere with each other’s logic or responsibilities. If multiple domains need to share services or data, those services should either be abstracted into the Shared domain or handled in Infrastructure.

This ensures that no domain unintentionally “leaks” logic or affects the behavior of another. It makes your codebase easier to maintain and scale as each domain evolves independently.

Conclusion: Take Your Laravel Development to the Stars 🌌

If you’re ready to build Laravel applications that are not only scalable and powerful but also fun to work with, "Domain-Driven Laravel 🛸 🔃" is the repository for you. It combines the elegance of Domain-Driven Design with Laravel's strength, all while adding a dash of cosmic humor 💀

Whether you’re a seasoned developer or just getting started with DDD, this package will help you organize your code, streamline your APIs, and provide a delightful development experience.

So what are you waiting for? Head over to the Domain-Driven Laravel 🛸 🔃 repository, and start building systems that are out of this world!

May your code always compile, and your APIs always return a 200! 🚀✨

Top comments (0)