File Structure Standards
Overview
This document defines the standardized file and directory structure for backend projects. Following these conventions ensures consistency, maintainability, and ease of navigation across the codebase.
Project Root Structure
project-root/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── tests/
├── vendor/
├── .env
├── .env.example
├── .gitignore
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
└── README.mdApp Directory Structure
The app/ directory contains the core application code:
app/
├── Console/
│ ├── Commands/
│ └── Kernel.php
├── Exceptions/
│ └── Handler.php
├── Http/
│ ├── Controllers/
│ │ ├── Api/
│ │ │ ├── V1/
│ │ │ │ ├── UserController.php
│ │ │ │ ├── PostController.php
│ │ │ │ └── AuthController.php
│ │ │ └── V2/
│ │ └── Web/
│ ├── Middleware/
│ │ ├── Authenticate.php
│ │ ├── CheckRole.php
│ │ └── RateLimitMiddleware.php
│ ├── Requests/
│ │ ├── Auth/
│ │ │ ├── LoginRequest.php
│ │ │ └── RegisterRequest.php
│ │ └── User/
│ │ ├── StoreUserRequest.php
│ │ └── UpdateUserRequest.php
│ ├── Resources/
│ │ ├── UserResource.php
│ │ ├── PostResource.php
│ │ └── UserCollection.php
│ └── Kernel.php
├── Models/
│ ├── User.php
│ ├── Post.php
│ ├── Comment.php
│ └── Traits/
│ ├── HasUuid.php
│ └── Searchable.php
├── Providers/
│ ├── AppServiceProvider.php
│ ├── AuthServiceProvider.php
│ ├── EventServiceProvider.php
│ └── RouteServiceProvider.php
├── Services/
│ ├── Auth/
│ │ ├── AuthService.php
│ │ └── TokenService.php
│ ├── User/
│ │ └── UserService.php
│ └── Notification/
│ └── NotificationService.php
├── Repositories/
│ ├── Contracts/
│ │ ├── UserRepositoryInterface.php
│ │ └── PostRepositoryInterface.php
│ ├── UserRepository.php
│ └── PostRepository.php
├── Events/
│ ├── UserCreated.php
│ └── PostPublished.php
├── Listeners/
│ ├── SendWelcomeEmail.php
│ └── NotifyAdmins.php
├── Jobs/
│ ├── ProcessUserImport.php
│ └── SendEmailNotification.php
├── Mail/
│ ├── WelcomeEmail.php
│ └── PasswordReset.php
├── Notifications/
│ ├── UserRegistered.php
│ └── PostPublished.php
├── Policies/
│ ├── UserPolicy.php
│ └── PostPolicy.php
├── Rules/
│ ├── ValidUsername.php
│ └── StrongPassword.php
└── Helpers/
├── DateHelper.php
└── StringHelper.phpDirectory Responsibilities
Console
Contains Artisan commands and the console kernel.
app/Console/
├── Commands/
│ ├── GenerateReport.php
│ ├── CleanupOldData.php
│ └── SyncExternalData.php
└── Kernel.phpNaming Convention:
- Use descriptive verb-noun combinations
- Suffix with appropriate action:
Generate,Cleanup,Sync,Process
Example:
// app/Console/Commands/GenerateMonthlyReport.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class GenerateMonthlyReport extends Command
{
protected $signature = 'report:generate-monthly';
protected $description = 'Generate monthly analytics report';
public function handle()
{
// Implementation
}
}Exceptions
Custom exception classes for specific error handling.
app/Exceptions/
├── Handler.php
├── Api/
│ ├── ApiException.php
│ ├── ResourceNotFoundException.php
│ └── ValidationException.php
└── Domain/
├── UserNotFoundException.php
└── InsufficientPermissionsException.phpExample:
// app/Exceptions/Api/ResourceNotFoundException.php
namespace App\Exceptions\Api;
use Exception;
class ResourceNotFoundException extends Exception
{
protected $message = 'The requested resource was not found';
protected $code = 404;
}HTTP Controllers
Controllers organized by API version and purpose.
app/Http/Controllers/
├── Api/
│ ├── V1/
│ │ ├── Auth/
│ │ │ ├── LoginController.php
│ │ │ ├── RegisterController.php
│ │ │ └── PasswordController.php
│ │ ├── User/
│ │ │ ├── UserController.php
│ │ │ └── UserProfileController.php
│ │ └── Post/
│ │ ├── PostController.php
│ │ └── PostCommentController.php
│ └── V2/
│ └── UserController.php
└── Controller.php (Base Controller)Naming Conventions:
- Use singular resource names:
UserController, notUsersController - Use descriptive names for nested resources:
PostCommentController - Group related controllers in subdirectories
Example:
// app/Http/Controllers/Api/V1/UserController.php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Http\Requests\User\StoreUserRequest;
use App\Services\User\UserService;
class UserController extends Controller
{
public function __construct(
private UserService $userService
) {}
public function index() { }
public function store(StoreUserRequest $request) { }
public function show($id) { }
public function update(UpdateUserRequest $request, $id) { }
public function destroy($id) { }
}HTTP Requests
Form request classes for validation.
app/Http/Requests/
├── Auth/
│ ├── LoginRequest.php
│ ├── RegisterRequest.php
│ └── ResetPasswordRequest.php
├── User/
│ ├── StoreUserRequest.php
│ ├── UpdateUserRequest.php
│ └── UpdateProfileRequest.php
└── Post/
├── StorePostRequest.php
└── UpdatePostRequest.phpNaming Convention:
- Prefix with action:
Store,Update,Delete - Suffix with
Request
Example:
// app/Http/Requests/User/StoreUserRequest.php
namespace App\Http\Requests\User;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8|confirmed',
];
}
}HTTP Resources
API resource transformers for consistent response formatting.
app/Http/Resources/
├── User/
│ ├── UserResource.php
│ ├── UserCollection.php
│ └── UserProfileResource.php
├── Post/
│ ├── PostResource.php
│ ├── PostCollection.php
│ └── PostDetailResource.php
└── Comment/
└── CommentResource.phpExample:
// app/Http/Resources/User/UserResource.php
namespace App\Http\Resources\User;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at->toIso8601String(),
'updated_at' => $this->updated_at->toIso8601String(),
];
}
}Models
Eloquent models representing database tables.
app/Models/
├── User.php
├── Post.php
├── Comment.php
├── Category.php
├── Tag.php
├── Traits/
│ ├── HasUuid.php
│ ├── Searchable.php
│ ├── HasSlug.php
│ └── SoftDeletesWithTrashed.php
├── Scopes/
│ ├── ActiveScope.php
│ └── PublishedScope.php
└── Observers/
├── UserObserver.php
└── PostObserver.phpNaming Convention:
- Use singular, capitalized names
- Match the table name (singular form)
Example:
// app/Models/User.php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Models\Traits\HasUuid;
class User extends Authenticatable
{
use HasUuid;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
}Services
Business logic layer separating concerns from controllers.
app/Services/
├── Auth/
│ ├── AuthService.php
│ ├── TokenService.php
│ └── PasswordResetService.php
├── User/
│ ├── UserService.php
│ ├── UserProfileService.php
│ └── UserPreferenceService.php
├── Post/
│ └── PostService.php
├── Payment/
│ ├── PaymentService.php
│ └── StripeService.php
└── Notification/
├── NotificationService.php
└── EmailService.phpExample:
// app/Services/User/UserService.php
namespace App\Services\User;
use App\Models\User;
use App\Repositories\UserRepository;
class UserService
{
public function __construct(
private UserRepository $userRepository
) {}
public function createUser(array $data): User
{
// Business logic
return $this->userRepository->create($data);
}
public function updateUser(User $user, array $data): User
{
// Business logic
return $this->userRepository->update($user, $data);
}
}Repositories
Data access layer abstracting database operations.
app/Repositories/
├── Contracts/
│ ├── RepositoryInterface.php
│ ├── UserRepositoryInterface.php
│ └── PostRepositoryInterface.php
├── BaseRepository.php
├── UserRepository.php
└── PostRepository.phpExample:
// app/Repositories/Contracts/UserRepositoryInterface.php
namespace App\Repositories\Contracts;
use App\Models\User;
interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function create(array $data): User;
public function update(User $user, array $data): User;
public function delete(User $user): bool;
}
// app/Repositories/UserRepository.php
namespace App\Repositories;
use App\Models\User;
use App\Repositories\Contracts\UserRepositoryInterface;
class UserRepository implements UserRepositoryInterface
{
public function findById(int $id): ?User
{
return User::find($id);
}
public function create(array $data): User
{
return User::create($data);
}
// ... other methods
}Events & Listeners
Event-driven architecture components.
app/Events/
├── Auth/
│ ├── UserRegistered.php
│ └── UserLoggedIn.php
└── Post/
├── PostCreated.php
└── PostPublished.php
app/Listeners/
├── Auth/
│ ├── SendWelcomeEmail.php
│ └── LogUserLogin.php
└── Post/
├── NotifySubscribers.php
└── UpdateSearchIndex.phpJobs
Queueable jobs for asynchronous processing.
app/Jobs/
├── User/
│ ├── ImportUsers.php
│ └── ExportUsers.php
├── Email/
│ ├── SendBulkEmail.php
│ └── SendNewsletter.php
└── Data/
├── SyncExternalData.php
└── GenerateReport.phpExample:
// app/Jobs/Email/SendWelcomeEmail.php
namespace App\Jobs\Email;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
private User $user
) {}
public function handle()
{
// Send email
}
}Policies
Authorization logic for model operations.
app/Policies/
├── UserPolicy.php
├── PostPolicy.php
└── CommentPolicy.phpExample:
// app/Policies/PostPolicy.php
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
class PostPolicy
{
public function view(User $user, Post $post): bool
{
return true;
}
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->isAdmin();
}
}Database Structure
database/
├── factories/
│ ├── UserFactory.php
│ └── PostFactory.php
├── migrations/
│ ├── 2024_01_01_000000_create_users_table.php
│ ├── 2024_01_01_000001_create_posts_table.php
│ └── 2024_01_02_000000_add_status_to_posts_table.php
└── seeders/
├── DatabaseSeeder.php
├── UserSeeder.php
├── PostSeeder.php
└── RoleSeeder.phpMigration Naming
Migrations should follow this pattern:
{timestamp}_{action}_{table_name}_table.php
Examples:
2024_01_01_000000_create_users_table.php
2024_01_02_000000_add_email_to_users_table.php
2024_01_03_000000_add_index_to_users_email.php
2024_01_04_000000_drop_old_column_from_users_table.phpRoutes Structure
routes/
├── api.php
├── web.php
├── console.php
└── channels.phpOrganization within route files:
// routes/api.php
// API V1 Routes
Route::prefix('v1')->group(function () {
// Public routes
Route::post('auth/login', [AuthController::class, 'login']);
Route::post('auth/register', [AuthController::class, 'register']);
// Authenticated routes
Route::middleware('auth:sanctum')->group(function () {
// User routes
Route::prefix('users')->group(function () {
Route::get('/', [UserController::class, 'index']);
Route::post('/', [UserController::class, 'store']);
Route::get('/{id}', [UserController::class, 'show']);
Route::put('/{id}', [UserController::class, 'update']);
Route::delete('/{id}', [UserController::class, 'destroy']);
});
// Post routes
Route::apiResource('posts', PostController::class);
});
});
// API V2 Routes
Route::prefix('v2')->group(function () {
// V2 routes
});Config Structure
config/
├── app.php
├── auth.php
├── cache.php
├── database.php
├── filesystems.php
├── logging.php
├── mail.php
├── queue.php
├── services.php
└── custom/
├── payment.php
├── api.php
└── notifications.phpTests Structure
tests/
├── Feature/
│ ├── Api/
│ │ ├── V1/
│ │ │ ├── Auth/
│ │ │ │ ├── LoginTest.php
│ │ │ │ └── RegisterTest.php
│ │ │ ├── User/
│ │ │ │ ├── CreateUserTest.php
│ │ │ │ ├── UpdateUserTest.php
│ │ │ │ └── DeleteUserTest.php
│ │ │ └── Post/
│ │ │ └── PostCrudTest.php
│ │ └── V2/
│ └── Web/
├── Unit/
│ ├── Services/
│ │ ├── UserServiceTest.php
│ │ └── AuthServiceTest.php
│ ├── Repositories/
│ │ └── UserRepositoryTest.php
│ └── Models/
│ └── UserTest.php
└── TestCase.phpTest Naming Convention:
- Suffix with
Test - Match the class being tested
- Use descriptive method names
Example:
// tests/Feature/Api/V1/User/CreateUserTest.php
namespace Tests\Feature\Api\V1\User;
use Tests\TestCase;
class CreateUserTest extends TestCase
{
public function test_can_create_user_with_valid_data()
{
// Test implementation
}
public function test_cannot_create_user_with_duplicate_email()
{
// Test implementation
}
}Storage Structure
storage/
├── app/
│ ├── public/
│ │ ├── avatars/
│ │ ├── documents/
│ │ └── uploads/
│ └── private/
├── framework/
│ ├── cache/
│ ├── sessions/
│ └── views/
└── logs/
└── laravel.logBest Practices
1. One Class Per File
Each file should contain only one class:
// Good
// app/Services/User/UserService.php
namespace App\Services\User;
class UserService
{
// Implementation
}
// Bad - Multiple classes in one file2. Namespace Matches Directory
File location should match namespace:
// File: app/Services/User/UserService.php
namespace App\Services\User; // Matches directory structure3. Consistent Naming
- Use PascalCase for class names
- Use singular nouns for models
- Use descriptive names indicating purpose
4. Group Related Files
Keep related functionality together:
app/Services/User/
├── UserService.php
├── UserProfileService.php
└── UserPreferenceService.php5. Separate Concerns
Different layers in separate directories:
app/
├── Http/Controllers/ # Request handling
├── Services/ # Business logic
├── Repositories/ # Data access
└── Models/ # Data models6. Version API Controllers
Keep API versions separated:
app/Http/Controllers/Api/
├── V1/
└── V2/7. Use Subdirectories
For large modules, create subdirectories:
app/Http/Controllers/Api/V1/
├── Auth/
│ ├── LoginController.php
│ ├── RegisterController.php
│ └── PasswordController.php
└── User/
├── UserController.php
└── UserProfileController.phpFile Naming Patterns
| Type | Pattern | Example |
|---|---|---|
| Controller | {Resource}Controller.php | UserController.php |
| Model | {Resource}.php | User.php |
| Request | {Action}{Resource}Request.php | StoreUserRequest.php |
| Resource | {Resource}Resource.php | UserResource.php |
| Service | {Resource}Service.php | UserService.php |
| Repository | {Resource}Repository.php | UserRepository.php |
| Event | {Resource}{Action}.php | UserCreated.php |
| Listener | {Action}{Resource}.php | SendWelcomeEmail.php |
| Job | {Action}{Resource}.php | ProcessUserImport.php |
| Policy | {Resource}Policy.php | UserPolicy.php |
| Migration | {date}_{action}_{table}_table.php | create_users_table.php |
| Test | {Class}Test.php | UserControllerTest.php |
Related Documentation
- API Design Standards - API design principles
- Naming Conventions - Naming standards
- Coding Standards - Code style guide
- Error Handling - Error handling patterns