amshahmy's avatar

Need Guidance on Choosing the Right Architecture for a Laravel Application

I’ve developed several Laravel applications using different architectural approaches, including the Repository Pattern, Domain-Driven Design (DDD), and the traditional MVC structure. I also make use of Form Requests and Data Transfer Objects (DTOs) to maintain clean input validation and data handling.

However, I have limited experience with testing strategies and how they integrate effectively with these architectures.

Currently, I’m building a new application using a modular, domain-driven design (DDD) approach, where each domain is organized into its own module containing models, repositories, and action classes for handling business logic.

I’d like to know whether this approach aligns with best practices in modern Laravel architecture, or if there are more efficient or scalable alternatives you’d recommend for building large, maintainable applications.

Any insights or real-world experiences would be highly appreciated.

1 like
11 replies
LaryAI's avatar
Level 58

Your approach—organizing your Laravel application using modular, domain-driven design (DDD) principles with models, repositories, and action classes per domain—is definitely aligned with best practices for scalable, maintainable applications. Let's address your core concerns and offer some suggestions:

1. DDD & Modularization in Laravel

DDD helps you clearly separate business logic from infrastructure concerns, which is especially beneficial for larger codebases. Modularization (organizing each domain as a separate module/folder/namespace) in Laravel, via packages or custom folder structure, is widely used and recommended for larger teams and projects.

Example Structure:

app/
  Domains/
    Blog/
      Models/
      Actions/
      Repositories/
      Requests/
    Billing/
      Models/
      Actions/
      Repositories/
      Requests/
  Helpers/

2. Patterns You Mentioned

  • Repository Pattern: Useful for abstracting data access, especially if you may swap Eloquent with another data source or need to support testing via mock repositories.
  • DTOs & Form Requests: Excellent for input validation and data transportation/encapsulation.
  • Action Classes: A modern approach for single-responsibility business logic. "Actions" (sometimes called "Services" or "Jobs") let you keep your controllers thin.

3. Testing Strategies

Testing is crucial no matter your architecture. DDD with the patterns above integrates nicely with automated testing:

  • Unit Tests: Test your action classes, repositories, and DTOs in isolation. Your structure (Repositories + Actions separated from Controllers) makes this easier.
  • Feature Tests: Use Laravel’s feature tests (tests/Feature) to test full endpoint functionality, simulating HTTP requests and responses.

Example: Testing Actions

public function test_user_creation_action()
{
    $data = new CreateUserData(name: 'John', email: '[email protected]');
    $result = app(CreateUserAction::class)->execute($data);

    $this->assertInstanceOf(User::class, $result);
    $this->assertEquals('John', $result->name);
}

Test Integration

  • Inject repositories/services as interfaces, and provide fake implementations in tests.
  • Use Laravel’s DatabaseMigrations/RefreshDatabase traits for DB state isolation.

4. Real-world Insights

  • Pros: Separation of concerns, easier testing, clear business boundaries, and better onboarding for new devs.
  • Cons: More boilerplate and increased abstraction, but this is outweighed by maintainability in large apps.
  • Packages to Consider:

5. Alternatives or Enhancements

  • Laravel Package Domains: You can go further and turn each domain into a local package, which allows independent development and reuse—invaluable when scaling even more.
  • Hexagonal (Ports & Adapters) Architecture: It’s similar to DDD and aligns well with your patterns (Repositories as ports, actions as application services).

Conclusion

You’re on the right path. The modular, DDD approach is one of the most scalable and maintainable options in modern Laravel development. Focus on clear boundaries, comprehensive tests, and automation/deployment pipelines.

If you’re new to testing, start by testing small service/action classes, then repositories, and then feature endpoints. This architecture will enable and reward good test coverage.

Reference Links:

Let me know if you want an in-depth real-world folder structure or sample code for a specific domain, or more detail on integrating testing into your workflow!

martinbean's avatar

I’d like to know whether this approach aligns with best practices in modern Laravel architecture, or if there are more efficient or scalable alternatives you’d recommend for building large, maintainable applications.

@amshahmy You should be using design patterns and architectural patterns if they actually solve the problem, not just throwing them in your application because you think they’re “best practice”.

2 likes
amshahmy's avatar

Currently, I am using as a best practice.

1 like
martinbean's avatar

🤦‍♂️

And again, “best practice” isn’t just throwing an architecture pattern into your application, just “because”. That’s probably the very opposite of a best practice.

1 like
vincent15000's avatar

First apply the KISS principle and improve your code with what you need to solve each problem one by one.

2 likes
jlrdw's avatar

I agree, these patterns can get out of hand. I have found MVC and using laravel conventions is the best practice.

I might have a special class to handle a complex search called from the controller, but only to keep the controller lean.

And just a suggestion, I advise not using a repository pattern, totally not needed.

There are other past post on this.

2 likes
ian_h's avatar

@amshahmy this really boils down to your application and what it's likely heading towards. IMO, it's easier to start with the likes of DDD than retro apply it.. it's something I'm in the midst of trying to architect at work for a 4 year old Laravel codebase that is.... let's just say, challenging... but there are an abundance of business areas of it that does, IMO, fit into the DDD realm.

Testing it is no different to any other test suite. You can add test groups in PHPUnit which can relate to your domains, so that you can run subsets of tests during a development cycle and then run the entire suite to make sure that everything is still fine.

Doing it this way then does also make it easier if you ever find the need to break parts out into SOA.

IMO, there's no right or wrong with what you're doing, if the application warrants it. If it's a simple small CRUD app that, then it would likely be overkill and I would probably stick with either plain ol' MVC or MVVM architecture, but if it really does warrant separating the domains and dealing with the overheads that that brings with it, then IMO, it's a fine approach... but as Martin says, don't just go a certain route "because it's cool".. there needs to be solid reasons behind these kinds of approaches.

2 likes

Please or to participate in this conversation.