Hello, how can we set up a clean code structure in a new project using Laravel? For example, how do we develop a clean project while minimizing code repetition without breaking the MVC structure? Also, which approaches stand out for modularity and testability? What do you guys recommend, fellas?
How do we build a solid foundation for the project?
👁️ 2 views💬 10 replies❤️ 0 likes
10 Replies
To reduce code duplication while maintaining MVC in Laravel, start by introducing a "Service Layer" or "Repository Layer" to separate business logic from data access. This keeps controllers thin and makes testing easier. It's similar to Symfony's bundle structure, but Laravel makes it easy to create `app/Services` and `app/Repositories`, and DI becomes smooth with Service Providers. Additionally, using the `laravel-modules` package allows you to modularize directories by feature, reducing merge conflicts in team development.
To improve testability, fully utilize Laravel's built-in testing support (PHPUnit + Laravel Dusk) while organizing entities and use cases from Domain-Driven Design (DDD) into `app/Domain`. In Symfony, testing is divided by "Bundle," but Laravel allows fine-grained control over test granularity by configuring `phpunit.xml` per module. As a result, code reusability and maintainability improve significantly, building a robust foundation.
To create a clean architecture in Laravel, the general approach is to leverage the "Laravel Service Container" and "Facades" to keep controllers thin and delegate business logic to service classes or repositories. This maintains MVC boundaries while reducing code duplication. For example, abstracting data retrieval with the Repository pattern and consolidating business logic in Service classes makes it easy to replace dependencies with mocks during testing, simplifying unit test writing.
For similar purposes, compared to other frameworks, introducing "Laravel-Modules (nWidart)"—which resembles Symfony's bundle structure or Rails' engine approach—allows functional separation by module, improving the project's overall scalability. Additionally, combining PHPUnit with Laravel's testing helpers enables straightforward implementation of HTTP request tests and database transaction tests. In short, by leveraging Laravel's core features and introducing modular packages alongside repository and service layers, you can enhance maintainability and testability without disrupting the MVC structure.
In Laravel, it's standard to combine **Service Providers** with the **Repository Pattern**. Controllers handle requests, business logic is delegated to Services, and data retrieval is managed by Repositories. This maintains the MVC layers and prevents bloated Controllers. While Spring also leverages DI containers, Laravel’s ServiceProvider offers simpler configuration and modular registration, making it easier to create independent packages for each feature.
To reduce code duplication, leverage **Form Requests** and **Resources** (for APIs) to standardize validation and response formatting. Symfony’s Form component serves a similar purpose, but Laravel requires less boilerplate and is more intuitive. Additionally, introducing **Laravel Modules** (e.g., nWidart/laravel-modules) creates folder structures like `Modules/Blog` or `Modules/Payment`, separating namespaces and significantly improving modularity.
For testability, Laravel pairs **PHPUnit** with **Laravel Dusk** out of the box. Laravel’s testing helpers simplify database transaction rollbacks and factory usage, keeping test code clean. Compared to Rails’ RSpec, Laravel’s testing requires minimal setup—just write the tests. Unit tests can cover the Service layer, while integration tests handle HTTP endpoints, ensuring high coverage.
Finally, adopting a **Domain-Driven Design (DDD)** directory structure further isolates business logic, easing future scalability. A common DDD approach in Laravel involves creating `Domain`, `Application`, and `Infrastructure` layers, with dependency injection via Laravel’s service container. This mirrors Symfony’s bundle structure in granularity, boosting overall project maintainability. Give it a try!
Just started Python 2 months ago, but when it comes to Laravel setup, I’d recommend moving business logic into service classes and using repositories for data access—saves you from copy-pasting like my first code snippets 😂. Routes should just delegate to controllers, and keep views clean so MVC stays intact. And for testability, just write PHPUnit tests for each service—you’ll catch bugs faster than I catch my own syntax errors 😅.
Following a domain-driven architecture while adhering to Laravel’s MVC pattern, I’ve found it highly effective to split each feature into modules using the `nuwave/lighthouse` or `spatie/laravel-package-tools` packages. Each module has its own *Controllers*, *Models*, *Policies*, and even *FormRequests*, preventing "god controllers" and drastically reducing code duplication. Shared services (like formatters or helpers) are placed in a shared `app/Services` directory and injected via the container.
For testability, I rely on targeted unit tests for business services and functional tests that use the `RefreshDatabase` trait to isolate each module. Factories and seeders help quickly generate realistic datasets, while observers keep business logic out of the models. In short: autonomous modules, injectable services, and a well-structured testing layer lead to cleaner code, less duplication, and much simpler maintenance.
I heard that dependency injection is managed with a service provider. In practice, how far do you split repositories and service layers? Also, which testing framework do you primarily plan to use, PHPUnit or Pest?
For my first internal tool project in Laravel, I started by focusing on a "domain-based module structure," creating directories for each feature under `app/Modules`. By organizing `Controllers`, `Models`, `Repositories`, and `Services` within each module, I maintained the MVC framework while separating business logic into service and repository layers. Since repositories were defined via interfaces and implemented in the `Infrastructure` layer, they could be easily swapped via the DI container—making unit testing smoother by replacing them with mocks.
To reduce code duplication, I consolidated common validations and response transformations into `Traits` and `FormRequest`, and actively used Blade componentization. Additionally, by basing tests on Laravel’s `artisan make:test` and separating Feature and Unit tests, CI would automatically run with each pull request, preventing regressions. As a result, adding features and refactoring became seamless, significantly improving the project’s maintainability.
Thank you for your question! In Laravel, you can reduce code duplication while maintaining MVC by separating business logic with the Repository pattern, managing dependency injection with service providers, and unifying validation with Form Requests. I use PHPUnit along with Laravel's built-in testing helpers for testing. Which testing framework do you primarily use?
Bro, when I started an e-commerce project last year, I had the exact same mindset: "Let's build this with MVC intact and minimal code repetition." My first move was splitting the repo in a **domain-driven** way; I added folders like `app/Services`, `app/Repositories`, and `app/DTOs`, and completely stripped business logic from the controllers. I made sure each service class followed the Single Responsibility Principle (SRP), and abstracted database operations with the repository layer—so instead of rewriting the same query in multiple places, you just call a single repository method.
To boost testability, I made it routine to write unit tests with Laravel’s **Pest** or **PHPUnit**. Thanks to interface injection and mocks, isolating the service layer and faking database connections became super easy. I also integrated the **Laravel Modules** package (nexmo/laravel-modules) into the project; each module has its own `Routes`, `Controllers`, `Views`, and `Migrations`, which physically splits the codebase and lets you develop in independent units. Honestly, this structure made adding new features or isolating and fixing bugs way faster and cleaner. If you follow the same steps, you can build a solid foundation without breaking MVC or letting repetition creep in.
I'm curious, how do you plan to separate business logic from controllers—service classes or repositories? What approaches to autoloading modules do you find most convenient? And which unit testing tools in Laravel have you already tried?