https://github.com/chinazagideon/trader-apis
Laravel APIs event driven payment microservice system with API key middleware for resource authorization and tenant isolation.
https://github.com/chinazagideon/trader-apis
api asynchronous deploy docker events gateway laravel microsevice observability outbox-pattern payments rest-api
Last synced: 3 months ago
JSON representation
Laravel APIs event driven payment microservice system with API key middleware for resource authorization and tenant isolation.
- Host: GitHub
- URL: https://github.com/chinazagideon/trader-apis
- Owner: chinazagideon
- Created: 2025-10-13T17:55:18.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-11-25T12:32:09.000Z (8 months ago)
- Last Synced: 2025-11-28T14:55:44.918Z (7 months ago)
- Topics: api, asynchronous, deploy, docker, events, gateway, laravel, microsevice, observability, outbox-pattern, payments, rest-api
- Language: PHP
- Homepage:
- Size: 1.3 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Payment Service APIs - Laravel Microservice Application
A Secured robust, event-driven payment microservices system API key middleware for resource authorization and tenant isolation. Built with a modular, event-driven architecture for scalability and maintainability.
## Core Architecture Principles
This application follows three fundamental architectural patterns:
1. **Module-Based Architecture** - Self-contained, auto-discovered modules
2. **Contract-Based Dependency Injection** - Interface-driven service binding
3. **Event-Driven Design** - Decoupled, configurable event processing
---
## Module Architecture
### Auto-Discovery System
Modules are automatically discovered from the `src/Modules/` directory. Each module is self-contained with its own:
- Service providers
- Database migrations
- Routes
- Controllers, Services, Repositories
- Configuration files
- Event listeners
**Module Structure:**
```
src/Modules/{ModuleName}/
├── Contracts/ # Service interfaces
├── Database/
│ ├── Migrations/ # Module migrations
│ ├── Seeders/ # Module seeders
│ └── Models/ # Eloquent models
├── Http/
│ ├── Controllers/ # API controllers
│ ├── Requests/ # Form validation
│ └── Resources/ # API resources
├── Providers/ # Service providers
├── Repositories/ # Data access layer
├── Services/ # Business logic
├── Events/ # Domain events
├── Listeners/ # Event listeners
├── routes/
│ └── api.php # Module routes
└── config/ # Module configuration
```
### Module Manager
The `ModuleManager` automatically discovers and registers modules:
```php
$moduleManager = app(ModuleManager::class);
$modules = $moduleManager->getModules();
$health = $moduleManager->getModuleHealth('User');
```
**Features:**
- Auto-discovery of modules in `src/Modules/`
- Service provider registration with priority support
- Health status monitoring
- Route and migration collection
- Module configuration loading
### Module Service Providers
Each module extends `BaseModuleServiceProvider` which provides:
- Automatic service registration
- Module namespace resolution
- Configuration merging
- Policy registration
**Example:**
```php
class UserServiceProvider extends BaseModuleServiceProvider
{
protected string $moduleNamespace = 'App\\Modules\\User';
protected array $configFiles = ['user'];
protected function registerServices(): void
{
// Bind interfaces to implementations
$this->app->bind(UserServiceInterface::class, UserService::class);
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}
}
```
---
## Contracts & Interface Binding
### Contract-Based Design
All services communicate through interfaces (contracts), enabling:
- Easy testing with mocks
- Implementation swapping
- Clear service boundaries
- Dependency inversion
### Service Binding Pattern
Services are bound to their interfaces in module service providers:
```php
// In UserServiceProvider
protected function registerServices(): void
{
// Singleton binding
$this->app->singleton(UserService::class);
// Interface binding
$this->app->bind(UserServiceInterface::class, UserService::class);
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}
```
### Dependency Injection
Controllers and services receive dependencies via constructor injection:
```php
class UserController extends Controller
{
public function __construct(
private UserServiceInterface $userService
) {}
}
```
### Sub-Module Service Registry
The `SubModuleServiceRegistry` automatically discovers and registers services that implement `SubModuleServiceContract`:
```php
// Services implementing SubModuleServiceContract are auto-registered
class UserBalanceService implements SubModuleServiceContract
{
public function getDefaultSubModuleName(): string
{
return 'user_balance';
}
}
// Access via registry
$registry = app(SubModuleServiceRegistry::class);
$service = $registry->get('user_balance');
```
---
## Event-Driven Architecture
### Event System Overview
The application uses a configurable event system that supports three processing modes:
- **Sync** - Immediate processing
- **Queue** - Background job processing
- **Scheduled** - Batch processing via scheduled events table
### Event Configuration
Events are configured in `config/events.php`:
```php
'events' => [
'investment_created' => [
'class' => InvestmentWasCreated::class,
'mode' => env('EVENT_INVESTMENT_MODE', 'queue'),
'queue' => env('EVENT_INVESTMENT_QUEUE', 'default'),
'priority' => 'high',
'listeners' => [
'create_transaction' => [
'class' => CreateTransactionForEntity::class,
'mode' => 'queue',
'tries' => 5,
'backoff' => [30, 60, 120],
],
],
],
],
```
### Notification Events Contract
All notification events implement `NotificationEventsContract`:
```php
interface NotificationEventsContract
{
public function getEntity();
public function getNotifiable();
public function getEventType(): string;
public function getChannels(): array;
public function getTitle(): string;
public function getMessage(): string;
// ... more methods
}
```
### Base Notification Event
Events extend `BaseNotificationEvent`:
```php
class UserWasCreatedEvent extends BaseNotificationEvent
{
public function __construct(public User $user) {}
public function getEventType(): string
{
return 'user_was_created';
}
public function getEntity()
{
return $this->user;
}
public function getNotifiable()
{
return $this->user;
}
public function getChannels(): array
{
return ['database', 'mail'];
}
}
```
### Configurable Listeners
Listeners implement `ConfigurableListenerInterface` to read configuration:
```php
class SendEntityNotification implements ShouldQueue, ConfigurableListenerInterface
{
use ConfigurableListener, InteractsWithQueue;
public function handle(NotificationEventsContract $event): void
{
// Listener reads its configuration from config/events.php
// Queue, retries, backoff all configured per listener
}
}
```
### Event Dispatcher
The `EventDispatcher` service routes events based on configuration:
```php
$eventDispatcher->dispatch(new InvestmentWasCreated($investment), 'investment_created');
```
The dispatcher checks `config/events.php` to determine:
- Processing mode (sync/queue/scheduled)
- Queue name
- Retry configuration
- Priority
---
## Notification Outbox Pattern
### Overview
The notification system uses an outbox pattern to ensure reliable notification delivery:
1. **Event Fired** → Listener publishes to `notification_outbox` table
2. **Outbox Processing** → Scheduled command processes pending notifications
3. **Notification Delivery** → Creates database notifications and queues emails
### Outbox Flow
```
Event → Listener → NotificationOutboxPublisher → notification_outbox table
↓
ProcessNotificationOutbox Command
↓
Database Notification + Queued Email
```
### Outbox Table
The `notification_outbox` table stores pending notifications:
- `event_type` - Type of event (e.g., 'user_was_created')
- `notifiable_type` / `notifiable_id` - Who receives the notification
- `entity_type` / `entity_id` - What triggered the notification
- `channels` - Delivery channels (database, mail, sms)
- `payload` - Notification data
- `status` - pending|processing|sent|failed
- `dedupe_key` - Prevents duplicate notifications
### Processing Command
The `notifications:outbox:process` command runs via Laravel scheduler:
```php
// routes/console.php
Schedule::command('notifications:outbox:process --limit=100')
->everyMinute()
->withoutOverlapping()
->runInBackground();
```
**Setup:**
```bash
# On server, add to crontab:
* * * * * cd /var/www/trader-apis && docker exec trader-apis-app php artisan schedule:run
```
---
## Module Management Commands
### List Modules
```bash
php artisan module:list
php artisan module:list --health
```
### Create Module
```bash
php artisan module:create Product --description="Product management"
```
### Migrations
```bash
php artisan module:migrate User
php artisan module:migrate --all
php artisan module:rollback User
php artisan module:migration:status
```
### Cache Module Providers
```bash
php artisan module:cache:providers
php artisan module:providers:list
```
---
## API Gateway
### Gateway Endpoints
- `GET /api/gateway/status` - Gateway status
- `GET /api/gateway/health` - Overall health check
- `GET /api/gateway/modules` - All modules info
- `GET /api/gateway/modules/{module}` - Specific module info
### Module Routes
Each module defines routes in `routes/api.php`:
```php
Route::prefix('api/v1/users')->group(function () {
Route::get('/', [UserController::class, 'index']);
Route::post('/', [UserController::class, 'store']);
});
```
---
## Database Architecture
### Module Migrations
Each module has its own migrations in `Database/Migrations/`. Migrations are automatically discovered and registered.
### Morph Maps
Polymorphic relationships use clean aliases via morph maps:
```php
// config/core.php
'morph_maps' => [
'user' => \App\Modules\User\Database\Models\User::class,
'investment' => \App\Modules\Investment\Database\Models\Investment::class,
]
```
### Database Connections
Modules can use separate database connections via configuration:
```php
// Module config
'database' => [
'connection' => env('USER_DB_CONNECTION', 'default'),
'prefix' => env('USER_DB_PREFIX', ''),
],
```
---
## Current Modules
- **Auth** - Authentication and authorization
- **User** - User management
- **Client** - Multi-client support
- **Investment** - Investment management
- **Transaction** - Transaction processing
- **Payment** - Payment processing
- **Funding** - Account funding
- **Withdrawal** - Withdrawal processing
- **Balance** - Balance management
- **Notification** - Notification system
- **Market** - Market data
- **Pricing** - Pricing engine
- **Currency** - Currency management
- **Category** - Transaction categories
- **Role** - Role-based access control
- **Dashboard** - Dashboard data
- **Swap** - Currency swapping
---
## Key Features
### 1. Module Auto-Discovery
Modules are automatically discovered and registered on application boot.
### 2. Contract-Based Services
All services use interfaces, enabling easy testing and implementation swapping.
### 3. Configurable Event Processing
Events can be processed sync, queued, or scheduled based on configuration.
### 4. Notification Outbox
Reliable notification delivery using the outbox pattern with scheduled processing.
### 5. Sub-Module Services
Automatic registration of services implementing `SubModuleServiceContract`.
### 6. Health Monitoring
Module health checks via API Gateway endpoints.
### 7. Module Migrations
Isolated migrations per module with rollback support.
---
## Development Workflow
### Creating a New Module
1. Generate module structure:
```bash
php artisan module:create Product
```
2. Define contracts in `Contracts/`:
```php
interface ProductServiceInterface {}
```
3. Implement services:
```php
class ProductService implements ProductServiceInterface {}
```
4. Bind in service provider:
```php
$this->app->bind(ProductServiceInterface::class, ProductService::class);
```
5. Create events:
```php
class ProductWasCreated extends BaseNotificationEvent {}
```
6. Register listeners in event service provider
7. Add routes in `routes/api.php`
8. Create migrations:
```bash
php artisan module:make:migration Product create_products_table
```
---
## Best Practices
### Module Design
- Keep modules focused on a single domain
- Use contracts for all service interfaces
- Minimize cross-module dependencies
- Each module should be independently testable
### Event Design
- Events should represent domain events (things that happened)
- Use descriptive event names (e.g., `UserWasCreated`, `InvestmentWasCreated`)
- Events should contain all data needed by listeners
- Store IDs, not full models, for serialization safety
### Service Design
- Services should implement interfaces
- Use dependency injection, not facades
- Keep services focused on business logic
- Repositories handle data access
### Testing
- Mock interfaces, not concrete classes
- Test modules in isolation
- Use contracts for test doubles
- Test event listeners separately
---
## Configuration
### Module Configuration
Each module can define `config/{module}.php`:
```php
return [
'module' => [
'name' => 'User',
'version' => '1.0.0',
],
'database' => [
'connection' => env('USER_DB_CONNECTION', 'default'),
],
];
```
### Event Configuration
Events are configured in `config/events.php` with per-event and per-listener settings.
### Notification Configuration
Notification channels and providers configured in `config/notification.php`.
---
This architecture provides a solid foundation for building scalable, maintainable applications with clear module boundaries and event-driven communication.