KUSHAL'S COLLECTION

Introducing Collections

An in-depth introduction to Collections, a modular Laravel application leveraging the nwidart/laravel-modules package. This article explores how modular architecture improves maintainability and details the structural layout.

Kushal Saha
Jul 12, 2026 · 10 min read

Welcome to our new Laravel application! Today, we are excited to introduce Collections, a modular feature set built using the powerful nwidart/laravel-modules package.

As applications grow, traditional monolithic structures—where all models, controllers, and services share the global app/ directory—can quickly become difficult to maintain. Modular architecture solves this by enforcing strict domain boundaries and separating features into distinct, self-contained modules. This approach ensures our codebase remains scalable, organized, and closely aligned with Domain-Driven Design (DDD) principles.

Let's look at how our primary Blog module is set up and why this architecture is a game-changer.

The Blog Module Structure

Our custom Blog module lives entirely inside the Modules/Blog directory. It encapsulates everything related to the blogging domain, acting almost like a micro-application within our main Laravel project. This high cohesion means you know exactly where to look when debugging or adding new blog features.

  • Routes & Controllers: Dedicated web.php and api.php files route traffic to module-specific controllers, keeping the global route files uncluttered.
  • Database Layer: The module contains its own migrations, model factories, and seeders, making the blog feature completely portable and easy to test in isolation.
  • Service Providers: The module registers its own bindings, event listeners, and configuration files via a dedicated BlogServiceProvider.
  • Views: Clean, isolated Blade templates are located directly in Modules/Blog/resources/views/.

Enforcing Clean Architecture

One of the primary reasons for adopting Collections and the modular approach is to strictly enforce Clean Architecture. Instead of relying on "fat controllers" or bloated models, we delegate business logic to dedicated classes within the module.

Action Classes and DTOs

Inside Modules/Blog/Actions and Modules/Blog/DTOs, we map out the specific capabilities of the blog. For example, when a user creates a new post, the controller simply handles the HTTP request and passes a Data Transfer Object to an Action class:

// Modules/Blog/Http/Controllers/PostController.php
public function store(StorePostRequest $request, CreatePostAction $action)
{
    $postDTO = PostData::fromRequest($request);
    $post = $action->execute($postDTO);

    return redirect()->route('blog.posts.show', $post);
}

This ensures that the core business logic (saving the post, triggering notifications, dispatching webhooks) lives exclusively inside the CreatePostAction. The codebase becomes incredibly predictable, highly testable, and strictly typed.

Headless and API Integration

While Collections can serve traditional Blade views, the modular structure is perfectly suited for modern, headless frontend integrations.

By utilizing the module's isolated api.php route file, we can serve structured JSON:API resources directly to frontend frameworks (like an Angular SPA). The module manages its own Resource classes in Modules/Blog/Transformers, ensuring that the data contract between the backend and the reactive UI remains consistent and tightly scoped to the blog domain.

Handling Module Assets with Vite

When we do serve server-rendered views, managing frontend assets efficiently is critical. Instead of dumping all CSS and JavaScript into the root resources folder and compiling a single massive bundle, we maintain isolated asset pipelines for each module.

By configuring a specific build directory in our vite.config.js, we can compile the Blog module's assets separately. Here is how you render Vite assets from a custom module inside your layout file using the module_vite helper:

{{-- Vite CSS --}}
{{ module_vite('build-blog', 'resources/assets/css/app.css') }}

{{-- Vite JS --}}
{{ module_vite('build-blog', 'resources/assets/js/app.js') }}

This approach ensures that a user browsing the blog only downloads the Tailwind CSS styles and JavaScript necessary for that specific module. It optimizes page load times and keeps bundle sizes exceptionally small across the wider application.

Looking Forward

By treating the Blog—and future Collections—as distinct modules, we are laying the groundwork for a highly maintainable system. Whether we are adding complex filtering, integrating AI moderation, or expanding the API for mobile clients, the modular foundation ensures our code remains clean and decoupled.

Stay tuned for more updates as we continue to build out our modular ecosystem!


Kushal Saha

Kushal Saha

I'm Kushal, a full-stack software engineer based in Kolkata specializing in the Laravel ecosystem and modern frontends. I advocate for clean and type-safe code, preferring to build scalable applications. I use this space to document my technical projects, share development insights, and explore the tools that power the web.