This document outlines the architectural decisions and patterns used in our Laravel-based applications. It serves as a guide for understanding how we structure our code and make architectural decisions.
- Modular Design: Organize code around business domains
- SOLID Principles: Follow software design principles for maintainable code
- Domain-Driven Design: Structure code around business domains
- Progressive Enhancement: Start simple, evolve as needed
We use a modular approach to organize our Laravel applications. Each module represents a distinct business domain and follows a consistent structure:
src/
ModuleName/
Console/
Commands/
Controllers/
Models/
Services/
Routes/
web.php
api.php
README.md
- Models: Domain entities and relationships
- Controllers: HTTP request/response handling
- Services: Business logic and domain operations
- Commands: Console tasks and scheduled jobs
- Routes: Module-specific endpoints
- README.md: Module documentation
src/
CurrencyExchange/
Console/
Commands/
DownloadCurrencyRates.php
Models/
CurrencyRate.php
Services/
CurrencyService.php
Routes/
api.php
README.mdWe follow Laravel's MVC pattern:
- Models: Business logic and data access
- Views: Presentation layer (Blade templates)
- Controllers: Request handling and response generation
We use services to:
- Encapsulate complex business logic
- Provide reusable functionality
- Keep controllers thin
- Enable better testing
Repositories are used to:
- Abstract data access
- Provide a consistent interface
- Make testing easier
- Centralize query logic
- Module Independence: Keep modules as independent as possible
- Clear Interfaces: Define clear boundaries between modules
- Shared Code: Use a common module for shared functionality
- Documentation: Maintain README files for each module
- Testing: Write tests at the module level
Create a new module when:
- The functionality represents a distinct business domain
- The code could be reused in other projects
- The feature set is large enough to warrant separation
- Different teams will work on different parts
Modules communicate through:
- Service classes
- Events and listeners
- Shared interfaces
- Message queues
- API endpoints
We begin with a monolithic application and:
- Organize code into modules
- Keep related code together
- Maintain clear boundaries
- Make modules self-contained
As the application grows:
- Extract modules when needed
- Maintain clear interfaces
- Keep documentation up to date
- Consider microservices for specific domains
- Unit tests for business logic
- Feature tests for HTTP endpoints
- Integration tests for module interactions
- End-to-end tests for critical paths
- Each module has its own README
- Architecture decisions are documented
- API documentation is maintained
- Code is self-documenting where possible