Service Container & Dependency Injection
Relevant source files
The following files were used as context for generating this wiki page:
The qashierwise-be application leverages Laravel's powerful dependency injection container to manage class dependencies and promote a clean, decoupled architecture. This design pattern is fundamental to the project, allowing for flexible and testable code by separating the construction of objects from their usage. Business logic is encapsulated within "Service" classes, which are then automatically injected into controllers and other classes where they are needed.
This approach adheres to the principles of inversion of control, where the framework is responsible for creating and "injecting" dependencies, rather than classes creating their own dependencies. This makes components like controllers leaner and more focused on handling HTTP requests, while the complex business logic resides in the service layer.
Sources: README.md:18, app/Http/Controllers/Api/Pos/OrderController.php:12-15, app/Services/OrderService.php
Service Registration and Resolution
The application primarily relies on Laravel's auto-wiring (or zero-configuration resolution) capabilities. This means that for most services, no explicit registration in a service provider is necessary. The container automatically inspects the class constructor for type-hinted dependencies and resolves them recursively.
Service Providers
The main entry point for bootstrapping services is through service providers. The project contains a standard AppServiceProvider. While it is currently empty, it serves as the designated place to bind interfaces to concrete implementations or register complex services that cannot be auto-wired.
// app/Providers/AppServiceProvider.php:14-24
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}
Sources: app/Providers/AppServiceProvider.php:14-24
Manual Resolution
While dependency injection via constructors is the preferred method, services can also be resolved manually from the container using the app() helper function. This is particularly useful in contexts outside of class constructors, such as in test files or scripts.
The following diagram illustrates the manual resolution process.
Sources: test_services.php:19
An example from test_services.php shows how OrderService is resolved manually:
// test_services.php:51
$orderService = app(\App\Services\OrderService::class);
Sources: test_services.php:51
Service Layer Architecture
The core business logic of the application is organized into a service layer located at app/Services/. These classes are responsible for specific business domains.
Key Service Classes
| Service Class | Description | Dependencies | Source File |
|---|---|---|---|
TemplateService | Manages WhatsApp message templates, including creation, validation, and interaction with the Meta Graph API. | config | [app/Services/TemplateService.php](https://github.com/adityabintang/qashierwise-be/blob/HEAD/app/Services/TemplateService.php) |
OrderService | Handles the business logic for creating and managing POS orders, including adding items and calculating totals. | ProductService, InventoryService (inferred) | [app/Services/OrderService.php](https://github.com/adityabintang/qashierwise-be/blob/HEAD/app/Services/OrderService.php) |
SubscriptionService | Manages user subscriptions and interacts with the Polar.sh API for checkout and customer portal sessions. | config, Http facade | [app/Services/SubscriptionService.php](https://github.com/adityabintang/qashierwise-be/blob/HEAD/app/Services/SubscriptionService.php) |
Sources: app/Services/TemplateService.php, app/Services/OrderService.php, app/Services/SubscriptionService.php
Service Dependencies
Services themselves can have dependencies, which are also resolved by the container. For example, TemplateService depends on configuration values to connect to the WhatsApp API.
// app/Services/TemplateService.php:12-18
public function __construct()
{
$this->apiVersion = config('whatsapp.api_version') ?? 'v22.0';
$this->accessToken = config('whatsapp.access_token') ?? '';
$this->businessAccountId = config('whatsapp.business_account_id') ?? '';
}
The setCredentials method also allows for dynamically changing these dependencies at runtime, which is useful for multi-tenant scenarios.
Sources: app/Services/TemplateService.php:12-26
Dependency Injection in Practice
Dependency injection is most prominently used to provide service classes to the application's controllers.
Constructor Injection in Controllers
Controllers type-hint services in their constructors. The service container automatically creates an instance of the service and injects it when the controller is instantiated.
The OrderController demonstrates this by injecting OrderService:
// app/Http/Controllers/Api/Pos/OrderController.php:12-15
public function __construct(
protected OrderService $orderService
) {
}
This makes the OrderService instance available as $this->orderService throughout the controller, ready to be used by methods like store() and addItem().
Sources: app/Http/Controllers/Api/Pos/OrderController.php:12-15
Class Relationship Diagram
The following diagram shows the dependency relationship where controllers depend on services, which in turn may depend on models or other services.
This diagram illustrates how OrderController uses an injected OrderService to perform business logic, which then interacts with the Order model.
Sources: app/Http/Controllers/Api/Pos/OrderController.php, app/Services/OrderService.php
Request Flow Sequence
The sequence diagram below shows the flow of creating a new order. The service container's role is implicit in the creation of OrderController and the injection of OrderService.
This sequence shows how a client request is handled by the OrderController, which delegates the core logic of order creation to the injected OrderService.
Sources: app/Http/Controllers/Api/Pos/OrderController.php:24-37, app/Services/OrderService.php:30-43
Conclusion
The project's use of Laravel's service container and dependency injection establishes a robust and scalable architecture. By abstracting business logic into service classes and injecting them where needed, the application achieves a clear separation of concerns. This makes the codebase easier to maintain, extend, and test, as components are loosely coupled and their dependencies are managed explicitly by the framework.