Appearance
Architecture Overview
Blindstrader is built as a microservices monorepo — each service has its own codebase, Docker container, database, and deployment lifecycle, but they all live in the same Git repository for coordinated development.
Microservices Map
| Service | Responsibility | Database | Port |
|---|---|---|---|
| Identity | SSO auth, RBAC, partner discovery, connections | Global MySQL | 8001 |
| Brand | Tenant federation, catalog management, permission enforcement | Per-Tenant MySQL | 8002 |
| Supplier | Supplier operations, pricing, inventory, fulfillment | Per-Tenant MySQL | 8003 |
| Supply Chain | Order orchestration, Saga coordination, fulfillment rules | Operational MySQL | 8004 |
| Payment | Stripe Connect, payment splits, ledgers, refunds | Financial MySQL | 8005 |
| Retailer | Customer storefronts, catalog overrides, local pricing | Per-Tenant MySQL | 8006 |
| Platform Ops | Billing, analytics, system administration (Filament) | Management MySQL | 8007 |
| Notification | Transactional email, SMS, Slack alerts | Utility MySQL | 8008 |
Core Architectural Principles
1. Database-per-Service
Each microservice has its own dedicated operational database. No two services share a database schema. Cross-service data access is via the event bus or authenticated HTTP API calls — never direct DB joins.
2. Database-per-Tenant
Every Brand, Supplier, and Retailer organisation also gets its own isolated MySQL database. One tenant's query load cannot affect another tenant's performance.
3. Event-Driven Communication
Services communicate asynchronously via Apache Kafka. Services emit events when something significant happens; other services subscribe and react. No synchronous service-to-service calls for core business logic.
4. Stateless Federation (Brand Service)
The Brand Service is a stateless routing layer. It holds no business data itself — when an API call arrives, it resolves the tenant, enforces permissions, and routes the query to the correct tenant database.
5. Saga Pattern for Distributed Transactions
Operations that span multiple services (e.g. place order → reserve inventory → capture payment → dispatch) use the Saga pattern. Each step emits an event; compensating transactions roll back partial state if any step fails.
Infrastructure Stack
| Component | Technology |
|---|---|
| Language / framework | PHP 8.3 + Laravel 11 |
| Admin panels | Filament 5 |
| Reverse proxy | Nginx/OpenResty with dynamic routing |
| Event bus | Apache Kafka |
| Cache / routing | Redis |
| Databases | MySQL (per-service, per-tenant) |
| Object storage | MinIO (S3-compatible) |
| Payments | Stripe Connect |
| Monitoring | Prometheus + Grafana + Loki + Promtail |
| Containerisation | Docker / Docker Compose / Kubernetes |
| CI/CD | GitHub Actions |
| Deployment | Ansible on Debian EC2 |
| IaC | Terraform |
Domain Routing
| Environment | Pattern | Example |
|---|---|---|
| Production | {service}.blindstrader.com | brand.blindstrader.com |
| Staging | {service}.stage.blindstrader.com | brand.stage.blindstrader.com |
| Local | {service}.blindstrader.test | brand.blindstrader.test |
| Docs | docs.blindstrader.com | — |
The Nginx reverse proxy uses environment variable substitution (${DOMAIN}) to generate the correct server_name directives at runtime.